/*
 * Copyright (c) 2021 Futurewei Technologies, Inc.
 *
 * clang2mpl is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan
 * PSL v2. You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the
 * Mulan PSL v2 for more details.
 */
#include "Clang2MapleVisitor.h"
#include "Clang2MapleOptions.h"

#include "clang/AST/APValue.h"
#include "clang/AST/AST.h"
#include "clang/AST/Attr.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APSInt.h"
#include "llvm/ADT/SmallVector.h"

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-warning-option"
#pragma GCC diagnostic ignored "-Wcast-qual"
#pragma GCC diagnostic ignored "-Wignored-qualifiers"
#pragma GCC diagnostic ignored "-Wreturn-type"
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic ignored "-Wpedantic"
#pragma GCC diagnostic ignored "-Wtype-limits"
#pragma GCC diagnostic ignored "-Wcovered-switch-default"
#pragma GCC diagnostic ignored "-Wsuggest-override"
#include "bin_mplt.h"
#include "mir_symbol.h"
#include "mir_type.h"
#include "mpl_logging.h"
#include "opcode_info.h"
#pragma GCC diagnostic pop

#if defined __has_cpp_attribute
#if __has_cpp_attribute(clang::fallthrough)
#define FALLTHROUGH [[clang::fallthrough]]
#else
#define FALLTHROUGH
#endif
#else
#define FALLTHROUGH
#endif

using namespace maple;

#define BITS_PER_BYTE 8

#define MULTIDIM_ARRAYS 1

static MIRStorageClass StorageClass2Mpl(clang::StorageClass SC);
static Opcode BinOpcode2Mpl(clang::BinaryOperatorKind Op, bool isSigned);
static bool isAssign(Opcode Op);
static bool isPointerType(MIRType *Ty);
static size_t getPointedToSize(MIRType *Ty);
static bool isOneElementVector(clang::QualType QT);
static bool isOneElementVector(const clang::Type *Ty);

#ifdef DEBUG
namespace maple {
extern MIRModule *theMIRModule;
}
#endif // DEBUG

MIRSymbol *Clang2MapleVisitor::VisitTranslationUnitDecl(
    const clang::TranslationUnitDecl *TU) {
  Context = &TU->getASTContext();

  setupBuiltinTypes();

  // Initial setup for Maple

  // Get source file
  clang::FileID MainFileID = Context->getSourceManager().getMainFileID();
  const clang::FileEntry *MainFile =
      Context->getSourceManager().getFileEntryForID(MainFileID);

  // Set source language
  Module = new MIRModule(MainFile->getName().str().c_str());
  Module->SetCurFunction(nullptr);

#ifdef DEBUG
  theMIRModule = Module;
#endif // DEBUG

  Module->SetSrcLang(Context->getLangOpts().CPlusPlus ? kSrcLangCPlusPlus
                                                      : kSrcLangC);

  // TODO: Do we need to setup the source files here? see whirl2mpl.cxx:2879

  Builder = new MIRBuilder(Module);

  // Create the builtin va_list type
  clang::TypedefDecl *VaListTypeDef = Context->getBuiltinVaListDecl();
  if (VaListTypeDef->isReferenced()) {
    Visit(VaListTypeDef->getUnderlyingType()->getAsRecordDecl());
  }

  // Visit all of the top-level children
  for (auto child = TU->decls_begin(); child != TU->decls_end(); ++child) {
    if (!child->isImplicit()) {
      Visit(*child);
    }
  }

  // Finalize the module
  Module->SetFlavor(kFeProduced);
  Module->SetNumFuncs(Module->GetFunctionList().size());
  if (UseAscii) {
    Module->OutputAsciiMpl("", ".mpl");
  } else {
    BinaryMplt BinMplt(*Module);
    std::string ModuleName = Module->GetFileName();
    std::string::size_type LastDot = ModuleName.find_last_of(".");
    std::string BaseName = ModuleName.substr(0, LastDot);
    BinMplt.GetBinExport().not2mplt = true;
    BinMplt.Export(BaseName + ".bpl");
  }

  return nullptr;
}

//
// Visit Decls
//

// Visit TypeDecls

MIRSymbol *Clang2MapleVisitor::VisitTypeDecl(const clang::TypeDecl *Ty) {
  LogInfo::MapleLogger() << "Error: Unhandled type decl\n";
#ifdef DEBUG
  Ty->dump();
#endif
  CHECK_FATAL(false, "Failing due to unsupported feature");
  return nullptr;
}

static bool TypeHasMayAlias(const clang::QualType srcType) {
  // Tagged types have declarations, and therefore may have attributes.
  auto *td = srcType->getAsTagDecl();
  if (td != nullptr && td->hasAttr<clang::MayAliasAttr>()) {
    return true;
  }
  clang::QualType qualType = srcType;
  while (auto *tt = qualType->getAs<clang::TypedefType>()) {
    if (tt->getDecl()->hasAttr<clang::MayAliasAttr>()) {
      return true;
    }
    qualType = tt->desugar();
  }
  return false;
}

MIRSymbol *
Clang2MapleVisitor::VisitRecordDecl(const clang::RecordDecl *Record) {
  // If there is not a complete definition, or this is not it, skip it.
  if (!Record || !Record->isCompleteDefinition() ||
      !Record->isThisDeclarationADefinition()) {
    return nullptr;
  }

  // If this record has already been fully defined, skip it.
  clang::QualType RecTy = Context->getRecordType(Record);
  auto it = TypeMap.find(RecTy.getTypePtr());
  if (it != TypeMap.end()) {
    MIRStructType *STy = static_cast<MIRStructType *>(
        GlobalTables::GetTypeTable().GetTypeFromTyIdx(it->second));
    if (!STy->IsIncomplete()) {
      return nullptr;
    }
  }

  // Process any decls inside of this record (inner struct)
  clang::DeclContext *DC = clang::TagDecl::castToDeclContext(Record);
  for (const auto *D : DC->decls()) {
    if (auto Inner = llvm::dyn_cast<clang::RecordDecl>(D)) {
      Visit(Inner);
    }
  }

  std::string TypeName = Record->getName().str();
  if (TypeName.empty()) { // Anonymous struct
    TypeName = "_anon" + std::to_string(++UnnamedCount);
  } else if (TypeName[0] == '.') {
    TypeName[0] = '_';
  }

  const clang::Type *StructTy = Record->getTypeForDecl();
  MIRStructType Struct(StructTy->isUnionType() ? kTypeUnion : kTypeStruct);
  FieldVector Fields;

  for (const auto *I : Record->fields()) {
    clang::QualType FieldQT = I->getType();
    TyIdx FieldTyIdx = type2MplIdx(FieldQT);
    FieldAttrs Attrs;
    TyIdxFieldAttrPair TyAttrPair;
    clang::CharUnits Alignment = Context->getDeclAlign(I);
    if (Alignment > Context->toCharUnitsFromBits(
                        Context->getTypeUnadjustedAlign(FieldQT))) {
      Attrs.SetAlign(Alignment.getQuantity());
    }
    if (FieldQT.isVolatileQualified()) {
      Attrs.SetAttr(FLDATTR_volatile);
    }
    if (FieldQT.isRestrictQualified()) {
      Attrs.SetAttr(FLDATTR_restrict);
    }
    if (FieldQT.isConstQualified()) {
      Attrs.SetAttr(FLDATTR_const);
    }
    if (isOneElementVector(FieldQT)) {
      Attrs.SetAttr(FLDATTR_oneelem_simd);
    }

    if (I->isBitField()) {
      MIRType *FieldTy =
          GlobalTables::GetTypeTable().GetTypeFromTyIdx(FieldTyIdx);
      unsigned BitWidth = I->getBitWidthValue(*Context);
      MIRBitFieldType BitFieldTy(BitWidth, FieldTy->GetPrimType());
      FieldTyIdx = GlobalTables::GetTypeTable().GetOrCreateMIRType(&BitFieldTy);
    }

    TyAttrPair = std::make_pair(FieldTyIdx, Attrs);
    std::string FieldName(I->getName().str());
    if (FieldName.empty()) {
      FieldName = "_no.name" + std::to_string(++UnnamedCount);
    } else if (FieldName[0] == '.') {
      FieldName[0] = '_';
    }

    GStrIdx FieldStrIdx =
        GlobalTables::GetStrTable().GetOrCreateStrIdxFromName(FieldName);
    Fields.push_back(std::make_pair(FieldStrIdx, TyAttrPair));
  }

  Struct.SetFields(Fields);

  recordFieldIDs(Record, Record, 0);

  MIRType *MTy = GlobalTables::GetTypeTable().GetOrCreateMIRTypeNode(Struct);
  GStrIdx StrIdx =
      GlobalTables::GetStrTable().GetOrCreateStrIdxFromName(TypeName);
  MTy->SetNameStrIdx(StrIdx);

  Module->GetTypeNameTab()->SetGStrIdxToTyIdx(StrIdx, MTy->GetTypeIndex());
  Module->PushbackTypeDefOrder(StrIdx);

  TypeMap[StructTy] = MTy->GetTypeIndex();

  // If this record was referenced while it was incomplete, update the pointer
  // type.
  auto UT = UnresolvedTypes.find(Record);
  if (UT != UnresolvedTypes.end()) {
    UT->second->SetPointedTyIdx(MTy->GetTypeIndex());
  }

  return nullptr;
}

MIRSymbol *
Clang2MapleVisitor::VisitTypedefDecl(const clang::TypedefDecl *Typedef) {
  return nullptr;
}

// Visit VarDecls

MIRSymbol *Clang2MapleVisitor::VisitVarDecl(const clang::VarDecl *Var) {
  std::string Name = Var->getName().str();
  if (Name[0] == '.') {
    Name[0] = '_';
  }

  MIRType *Ty = type2Mpl(Var->getType());

  MIRSymbol *Symbol;

  if (Var->isLocalVarDecl()) {
    if (Var->hasExternalStorage()) {
      // If it is referring to an existing global, use it, else create it.
      Symbol = Builder->GetGlobalDecl(Name);
      if (!Symbol) {
        Symbol = Builder->CreateGlobalDecl(Name, *Ty);
      }
      Symbol->SetAttr(ATTR_extern);
    } else {
      // If a local with this name already exists (from a different scope),
      // append a number so that they are unique.
      if (Builder->GetLocalDecl(Name)) {
        Name = Name + "." + std::to_string(++UnnamedCount);
      }
      Symbol = Builder->CreateLocalDecl(Name, *Ty);

      if (Var->isStaticLocal()) {
        Symbol->SetStorageClass(kScPstatic);
        if (Var->hasInit()) {
          Initializer = true;
          if (MIRConst *Init = getInitializer(Ty, Var->getInit())) {
            Init = expandConstants(Ty, Init);
            Symbol->SetKonst(Init);
          }
          Initializer = false;
        }
      }
    }
    if (Var->isUsed()) {  // locals
      Symbol->SetAttr(ATTR_used);
    }
    if (Var->isImplicit()) {
      Symbol->SetAttr(ATTR_implicit);
    }
  } else { // Global

    Symbol = Builder->GetOrCreateGlobalDecl(Name, *Ty);
    if (Var->isThisDeclarationADefinition() || !Var->isKnownToBeDefined()) {
      // Set the type here in case a previous declaration had an incomplete
      // array type and the definition has the complete type.
      Symbol->SetTyIdx(Ty->GetTypeIndex());
      Symbol->SetStorageClass(StorageClass2Mpl(Var->getStorageClass()));
      if (Var->hasInit()) {
        Initializer = true;
        if (MIRConst *Init = getInitializer(Ty, Var->getInit())) {
          Init = expandConstants(Ty, Init);
          Symbol->SetKonst(Init);
        }
        Initializer = false;
      }
    }

    if (Var->isWeak()) {
      Symbol->SetAttr(ATTR_weak);
    }
    if (Var->isUsed()) {  // locals
      Symbol->SetAttr(ATTR_used);
    }
    if (Var->isImplicit()) {
      Symbol->SetAttr(ATTR_implicit);
    }
  }

  if (Var->getType().isConstQualified()) {
    Symbol->SetAttr(ATTR_const);
  }
  if (Var->getType().isRestrictQualified()) {
    Symbol->SetAttr(ATTR_restrict);
  }
  if (Var->getType().isVolatileQualified()) {
    Symbol->SetAttr(ATTR_volatile);
  }

  if (!Var->getType()->isIncompleteType()) {
    int64_t NaturalAlignment =
        Context
            ->toCharUnitsFromBits(
                Context->getTypeUnadjustedAlign(Var->getType()))
            .getQuantity();

    // Get alignment from the decl
    TypeAttrs Attrs = Symbol->GetAttrs();
    if (unsigned int AlignmentBits = Var->getMaxAlignment()) {
      unsigned int Alignment = AlignmentBits / BITS_PER_BYTE;
      if (Alignment > NaturalAlignment) {
        Attrs.SetAlign(Alignment);
      }
    }

    // Get alignment from the type
    if (unsigned int AlignmentBits =
            Context->getTypeAlignIfKnown(Var->getType())) {
      unsigned int Alignment = AlignmentBits / BITS_PER_BYTE;
      if (Alignment > Attrs.GetAlign() && Alignment > NaturalAlignment) {
        Attrs.SetAlign(Alignment);
      }
    }

    Symbol->SetAttrs(Attrs);
  }

  if (isOneElementVector(Var->getType())) {
    Symbol->SetAttr(ATTR_oneelem_simd);
  }

  if (const auto *PT = Var->getType()
                           .getDesugaredType(*Context)
                           ->getAs<clang::PointerType>()) {
    if (const auto FT = PT->getPointeeType()
                            .getDesugaredType(*Context)
                            ->getAs<clang::FunctionType>()) {
      if (isOneElementVector(FT->getReturnType())) {
        Symbol->SetAttr(ATTR_oneelem_simd);
      }
    }
  }

  if (const auto *SA = Var->getAttr<clang::SectionAttr>()) {
    UStrIdx StrIdx = GlobalTables::GetUStrTable().GetOrCreateStrIdxFromName(
        SA->getName().str());
    Symbol->sectionAttr = StrIdx;
  }

  Symbol->SetSKind(kStVar);
  SrcPosition Pos = sourceLocation2Mpl(Var->getLocation());
  Symbol->SetSrcPosition(Pos);

  DeclMap.insert({Var, Symbol});

  return Symbol;
}

MIRSymbol *Clang2MapleVisitor::VisitParmVarDecl(const clang::ParmVarDecl *Var) {
  MIRType *Ty = type2Mpl(Var->getType());
  std::string Name = Var->getName().str();
  if (Name.empty()) {
    Name = "arg|" + std::to_string(Var->getFunctionScopeIndex());
  }
  if (Var->isKNRPromoted()) {
    Name = Name + ".kr";
  }
  MIRSymbol *Symbol = Builder->GetOrCreateLocalDecl(Name, *Ty);
  Symbol->SetStorageClass(kScFormal);
  SrcPosition Pos = sourceLocation2Mpl(Var->getLocation());
  Symbol->SetSrcPosition(Pos);

  if (Var->isUsed()) {
    Symbol->SetAttr(ATTR_used);
  }
  if (isOneElementVector(Var->getType())) {
    Symbol->SetAttr(ATTR_oneelem_simd);
  }
  if (Var->getType().isConstQualified()) {
    Symbol->SetAttr(ATTR_const);
  }
  if (Var->getType().isRestrictQualified()) {
    Symbol->SetAttr(ATTR_restrict);
  }
  if (Var->getType().isVolatileQualified()) {
    Symbol->SetAttr(ATTR_volatile);
  }

  DeclMap.insert({Var, Symbol});

  return Symbol;
}

void
Clang2MapleVisitor::SetFunctionAttrs(MIRFunction *Func, const clang::FunctionDecl *Decl) {
  if (Decl->isUsed()) {
    Func->SetAttr(FUNCATTR_used);
  }
  if (Decl->isImplicit()) {
    Func->SetAttr(FUNCATTR_implicit);
  }
  if (Decl->isInlineSpecified()) {
    Func->SetAttr(FUNCATTR_inline);
  } else if (Decl->hasAttr<clang::NoInlineAttr>()) {
    Func->SetAttr(FUNCATTR_noinline);
  }
  if (Decl->isVariadic()) {
    Func->SetAttr(FUNCATTR_varargs);
  }
  if (Decl->isNoReturn()) {
    Func->SetAttr(FUNCATTR_noreturn);
  }
  if (Decl->isWeak()) {
    Func->SetAttr(FUNCATTR_weak);
  }
  if (Decl->isStatic()) {
    Func->SetAttr(FUNCATTR_static);
  }
  if (Decl->getStorageClass() == clang::SC_Extern) {
    Func->SetAttr(FUNCATTR_extern);
  }
  if (isOneElementVector(Decl->getReturnType())) {
    Func->SetAttr(FUNCATTR_oneelem_simd);
  }
  // C++ attributes
  if (Decl->isVirtualAsWritten()) {
    Func->SetAttr(FUNCATTR_virtual);
  }
  if (Decl->isDeletedAsWritten()) {
    Func->SetAttr(FUNCATTR_delete);
  }
  if (Decl->isPure()) {
    Func->SetAttr(FUNCATTR_pure);
  }
  if (Decl->isDefaulted()) {
    Func->SetAttr(FUNCATTR_default);
  }
  if (Decl->getKind() == clang::Decl::CXXConstructor) {
    Func->SetAttr(FUNCATTR_constructor);
  }
  if (Decl->getKind() == clang::Decl::CXXDestructor) {
    Func->SetAttr(FUNCATTR_destructor);
  }
}

MIRSymbol *
Clang2MapleVisitor::VisitFunctionDecl(const clang::FunctionDecl *Func) {
  // Ignore Maple builtins' prototypes
  if (Func->getName().startswith("__builtin")) {
    return nullptr;
  }

  std::string Name = Func->getName().str();
  if (Name[0] == '.') {
    Name[0] = '_';
  }

  TyIdx FuncTyIdx = type2MplIdx(Func->getType());
  MIRFuncType *FuncType = static_cast<MIRFuncType *>(
      GlobalTables::GetTypeTable().GetTypeFromTyIdx(FuncTyIdx));

  MIRSymbol *FuncSym = GlobalTables::GetGsymTable().CreateSymbol(kScopeGlobal);
  GStrIdx StrIdx = Builder->GetOrCreateStringIndex(Name);
  FuncSym->SetNameStrIdx(StrIdx);
  GlobalTables::GetGsymTable().AddToStringSymbolMap(*FuncSym);
  FuncSym->SetStorageClass(kScText);
  FuncSym->SetSKind(kStFunc);
  SrcPosition Pos = sourceLocation2Mpl(Func->getLocation());
  FuncSym->SetSrcPosition(Pos);
  FuncSym->SetAppearsInCode(true);

  MIRFunction *MFunc = Builder->GetCurrentFuncCodeMp()->New<MIRFunction>(
      Module, FuncSym->GetStIdx());
  MFunc->SetMIRFuncType(FuncType);
  MIRType *RetTy =
      GlobalTables::GetTypeTable().GetTypeFromTyIdx(FuncType->GetRetTyIdx());
  MIRType *OrigRetTy = type2Mpl(Func->getReturnType());
  MFunc->SetReturnStruct(*RetTy);
  MFunc->SetPuidx(GlobalTables::GetFunctionTable().GetFuncTable().size());
  MFunc->SetPuidxOrigin(MFunc->GetPuidx());

  SetFunctionAttrs(MFunc, Func);

  if (Func->isVariadic()) {
    MFunc->SetVarArgs();
  }

  GlobalTables::GetFunctionTable().GetFuncTable().push_back(MFunc);
  FuncSym->SetTyIdx(FuncTyIdx);
  FuncSym->SetFunction(MFunc);

  if (Func->isWeak()) {
    FuncSym->SetAttr(ATTR_weak);
  }

  MIRFunction *LastFunc = Module->CurFunction();
  Module->SetCurFunction(MFunc);
  Builder->SetCurrentFunction(*MFunc);
  MFunc->AllocSymTab();
  MFunc->AllocPregTab();
  MFunc->AllocTypeNameTab();
  MFunc->AllocLabelTab();

  DeclMap.insert({Func, FuncSym});

  unsigned int firstParam = 0;
  if (OrigRetTy->GetSize() > 16) {
    MFunc->SetFirstArgReturn();
    firstParam = 1;

    MIRType *RetAddrTy = GlobalTables::GetTypeTable().GetTypeFromTyIdx(
        FuncType->GetNthParamType(0));
    MIRSymbol *Symbol = Builder->GetOrCreateLocalDecl("retval.0", *RetAddrTy);
    Symbol->SetStorageClass(kScFormal);
    SrcPosition Pos = sourceLocation2Mpl(Func->getLocation());
    Symbol->SetSrcPosition(Pos);

    FormalDef RetVal(Symbol->GetNameStrIdx(), Symbol, RetAddrTy->GetTypeIndex(),
                     TypeAttrs());
    MFunc->AddFormalDef(RetVal);
  }

  size_t NumParams = FuncType->GetParamTypeList().size();
  for (uint32 i = firstParam; i < NumParams; i++) {
    MIRSymbol *Param = Visit(Func->getParamDecl(i - firstParam));
    Param->SetTyIdx(FuncType->GetNthParamType(i));
    FormalDef FormalDef(Param->GetNameStrIdx(), Param,
                        FuncType->GetNthParamType(i),
                        FuncType->GetNthParamAttrs(i));
    MFunc->AddFormalDef(FormalDef);
  }

  if (Func->isThisDeclarationADefinition() && Func->hasBody()) {
    BlockNode *FuncBody = Builder->GetCurrentFuncCodeMp()->New<BlockNode>();
    // Insert conversions for K&R style parameters
    for (uint32 i = firstParam; i < NumParams; i++) {
      const clang::ParmVarDecl *Parm = Func->getParamDecl(i - firstParam);
      if (Parm->isKNRPromoted()) {
        MIRType *ParmTy = type2Mpl(Parm->getType());
        MIRType *KRTy = MFunc->GetNthParamType(i);
        // Create a new local with the correct type.
        MIRSymbol *ParmSym =
            Builder->GetOrCreateLocalDecl(Parm->getNameAsString(), *ParmTy);

        // Convert from the K&R type to the parameter's type.
        StmtNode *ParmInit = Builder->CreateStmtDassign(
            *ParmSym, 0,
            createNumericCast(
                ParmTy, Builder->CreateExprDread(
                            *KRTy, 0, *MFunc->GetFormalDefAt(i).formalSym)));
        FuncBody->AddStatement(ParmInit);

        // Update the map so that uses of the parameter use this new symbol.
        DeclMap[Parm] = ParmSym;
      }
    }

    // Insert function body
    Result BodyRes = Visit(Func->getBody());
    FuncBody->SetSrcPos(sourceLocation2Mpl(BodyRes.getLoc()));
    BlockNode *Body = static_cast<BlockNode *>(BodyRes.getNode());
    FuncBody->AppendStatementsFromBlock(*Body);
    MFunc->SetBody(FuncBody);
    if (VerifyMaple) {
      ASSERT(FuncBody->Verify(), "Verify failed");
    }

    // If the last instruction of the body is not a return, add a return, with a
    // zero value if there is a non-void return type.
    if (!FuncBody->GetLast() || FuncBody->GetLast()->GetOpCode() != OP_return) {
      BaseNode *RetVal = nullptr;
      if (RetTy->GetPrimType() != PTY_void) {
        if (PrimitiveType(RetTy->GetPrimType()).IsVector()) {
          RetVal = createVectorZero(RetTy);
        } else {
          if (!RetTy->IsScalarType()) {
            RetTy = GlobalTables::GetTypeTable().GetUInt32();
          }
          MIRConst *Zero = createZero(RetTy);
          RetVal = Builder->CreateConstval(Zero);
        }
      }
      NaryStmtNode *Return = Builder->CreateStmtReturn(RetVal);
      Return->SetSrcPos(sourceLocation2Mpl(Func->getEndLoc()));
      FuncBody->AddStatement(Return);
    }
  }

  Module->AddFunction(MFunc);
  Module->SetCurFunction(LastFunc);

  return FuncSym;
}

MIRSymbol *Clang2MapleVisitor::VisitEnumDecl(const clang::EnumDecl *Enum) {
  // Enums are just converted to constants when used, so do nothing here
  return nullptr;
}

MIRSymbol *Clang2MapleVisitor::VisitEnumConstantDecl(
    const clang::EnumConstantDecl *EnumConst) {
  // Enums are just converted to constants when used, so do nothing here
  return nullptr;
}

MIRSymbol *Clang2MapleVisitor::VisitFileScopeAsmDecl(
    const clang::FileScopeAsmDecl *AsmDecl) {
  MapleString AsmString(AsmDecl->getAsmString()->getString().str(),
                        Module->GetMemPool());
  Module->GetAsmDecls().push_back(AsmString);
  return nullptr;
}

//
// Visit Statements
//

Result Clang2MapleVisitor::VisitStmt(const clang::Stmt *S) {
  LogInfo::MapleLogger() << "Error: Unhandled statement\n";
#ifdef DEBUG
  S->dump();
#endif
  CHECK_FATAL(false, "Failing due to unsupported feature");
  return Result(S->getBeginLoc());
}

Result Clang2MapleVisitor::VisitGCCAsmStmt(const clang::GCCAsmStmt *GAS) {
  AsmNode *Asm = Builder->GetCurrentFuncCodeMp()->New<AsmNode>(
      Builder->GetCurrentFuncCodeMpAllocator());
  Result Res(Asm, GAS->getID(*Context), GAS->getBeginLoc());
  Asm->asmString = MapleString(GAS->generateAsmString(*Context),
                               Builder->GetCurrentFuncCodeMp());

  // Inputs
  for (unsigned int InNum = 0; InNum < GAS->getNumInputs(); InNum++) {
    Result InRes = Visit(GAS->getInputExpr(InNum));
    BaseNode *InNode = nullptr;
    if (GAS->getInputConstraint(InNum).contains('m')) {
      InNode = getNodeAsAddrOf(InRes);
    } else {
      InNode = getNodeAsRVal(InRes);
    }
    Asm->PushOpnd(InNode);
    Res.appendStmts(InRes);

    UStrIdx StrIdx = GlobalTables::GetUStrTable().GetOrCreateStrIdxFromName(
        GAS->getInputConstraint(InNum).str());
    Asm->inputConstraints.push_back(StrIdx);
  }

  // Outputs
  for (unsigned int OutNum = 0; OutNum < GAS->getNumOutputs(); OutNum++) {
    Result OutRes = Visit(GAS->getOutputExpr(OutNum));
    MIRSymbol *Sym = nullptr;
    if (OutRes.isSym()) {
      Sym = OutRes.getSym();
    }

    if (!Sym || !Sym->IsLocal()) {
      // Generate a temporary for the output, then assign that to the original
      // expression after the asm node.
      Sym = Builder->GetOrCreateLocalDecl(
          "_asmout_" + std::to_string(GAS->getID(*Context)) + "_" +
              std::to_string(OutNum),
          *OutRes.getValueTy());
      Sym->SetAttrs(OutRes.getValueTyAttrs());

      StmtNode *Assign = nullptr;

      // If this is a read/write, copy the initial value into the temp before
      // the asm node.
      if (GAS->isOutputPlusConstraint(OutNum)) {
        Assign = Builder->CreateStmtDassign(*Sym, 0, getNodeAsRVal(OutRes));
        Assign->SetSrcPos(sourceLocation2Mpl(GAS->getBeginLoc()));
        Res.appendStmtBefore(Assign);
      }

      BaseNode *ReadTmp = Builder->CreateExprDread(*OutRes.getValueTy(), *Sym);
      if (OutRes.isSym()) {
        Assign = Builder->CreateStmtDassign(*OutRes.getSym(), OutRes.getField(),
                                            ReadTmp);
      } else if (OutRes.isDeref()) {
        Assign = Builder->CreateStmtIassign(
            *OutRes.getAddrTy(), OutRes.getField(), OutRes.getAddr(), ReadTmp);
      } else {
        getNodeAsRVal(OutRes)->Dump();
        ASSERT(OutRes.isDeref() || OutRes.isSym(),
               "Error: output of asm is neither deref or symbol");
      }
      Assign->SetSrcPos(sourceLocation2Mpl(GAS->getBeginLoc()));
      Res.appendStmtAfter(Assign);
    }
    CallReturnPair OutPair(Sym->GetStIdx(), RegFieldPair(OutRes.getField(), 0));
    Asm->asmOutputs.push_back(OutPair);

    UStrIdx StrIdx = GlobalTables::GetUStrTable().GetOrCreateStrIdxFromName(
        GAS->getOutputConstraint(OutNum).str());
    Asm->outputConstraints.push_back(StrIdx);

    // Outputs with '+' are also added to the input list
    if (GAS->isOutputPlusConstraint(OutNum)) {
      BaseNode *InNode = Builder->CreateExprDread(*OutRes.getValueTy(), *Sym);
      Asm->PushOpnd(InNode);
      Asm->inputConstraints.push_back(StrIdx);
    }
  }

  // Clobbers
  for (unsigned int C = 0; C < GAS->getNumClobbers(); C++) {
    UStrIdx StrIdx = GlobalTables::GetUStrTable().GetOrCreateStrIdxFromName(
        GAS->getClobber(C).str());
    Asm->clobberList.push_back(StrIdx);
  }

  // Labels
  for (unsigned int L = 0; L < GAS->getNumLabels(); L++) {
    LabelIdx Label = Builder->GetOrCreateMIRLabel(GAS->getLabelName(L).str());
    Asm->gotoLabels.push_back(Label);
  }

  if (GAS->isVolatile())
    Asm->SetQualifier(kASMvolatile);
  if (GAS->isAsmGoto())
    Asm->SetQualifier(kASMgoto);

  return Res;
}

Result Clang2MapleVisitor::VisitCompoundStmt(const clang::CompoundStmt *CS) {
  BlockNode *Block = Builder->GetCurrentFuncCodeMp()->New<BlockNode>();

  for (clang::Stmt *S : CS->body()) {
    Result Res = Visit(S);
    addToBlock(Block, Res);
  }

  return Result(Block, CS->getID(*Context), CS->getBeginLoc());
}

Result Clang2MapleVisitor::VisitDeclStmt(const clang::DeclStmt *DS) {
  Result Res(DS->getBeginLoc());
  for (const auto *D : DS->decls()) {
    MIRSymbol *Sym = Visit(D);
    if (const clang::VarDecl *VD = llvm::dyn_cast<clang::VarDecl>(D)) {
      if (VD->isLocalVarDecl() && !VD->isStaticLocal()) {
        // Handle allocating stack space for a variable-length array
        if (const clang::VariableArrayType *VarArrTy =
                llvm::dyn_cast<clang::VariableArrayType>(VD->getType())) {
          Result VLASizeRes = Visit(VarArrTy->getSizeExpr());
          BaseNode *AllocaSize = getNodeAsRVal(VLASizeRes);
          if (GetPrimTypeSize(AllocaSize->GetPrimType()) != GetPrimTypeSize(IntPointerTy->GetPrimType())) {
            AllocaSize = Builder->CreateExprTypeCvt(OP_cvt, IntPointerTy->GetPrimType(), AllocaSize->GetPrimType(), *AllocaSize);
          }
          Res.appendStmts(VLASizeRes);
          MIRType *Ty = type2Mpl(VD->getType());
          size_t Scale =
              static_cast<MIRPtrType *>(Ty)->GetPointedType()->GetSize();
          if (Scale > 1) { // Skip multiplication for 1 (char) and 0 (void)
            ConstvalNode *Const = Builder->CreateIntConst(Scale, PointerPrimTy);
            AllocaSize = Builder->CreateExprBinary(OP_mul, *IntPointerTy,
                                                   AllocaSize, Const);
          }
          BaseNode *Alloca =
              Builder->CreateExprUnary(OP_alloca, *Ty, AllocaSize);
          Alloca->SetPrimType(PointerPrimTy);
          StmtNode *Assign = Builder->CreateStmtDassign(*Sym, 0, Alloca);
          Res.appendStmtAfter(Assign);
        }

        // Handle initialization
        if (VD->hasInit()) {
          const clang::Expr *InitExpr = VD->getInit();
          Result InitRes = Visit(InitExpr);
          BaseNode *Init = getNodeAsRVal(InitRes);
          Res.appendStmts(InitRes);
          if (!Init) {
            continue;
          }
          if (Sym->GetType()->GetKind() == kTypeStruct ||
              Sym->GetType()->GetKind() == kTypeUnion) {
            MIRStructType *StructTy =
                static_cast<MIRStructType *>(Sym->GetType());
            assignStruct(Res, Sym, nullptr, StructTy, Init);
          } else if (Sym->GetType()->GetKind() == kTypeArray) {
            MIRArrayType *ArrayTy = static_cast<MIRArrayType *>(Sym->GetType());
            AddrofNode *BaseAddr = Builder->CreateExprAddrof(0, *Sym);
            BaseAddr->SetPrimType(PointerPrimTy);
            assignArray(Res, BaseAddr, ArrayTy, Init);
          } else if (PrimitiveType(Sym->GetType()->GetPrimType()).IsVector()) {
            assignVector(Res, Sym, Sym->GetType(), Init);
          } else {
            DassignNode *Assign =
                Builder->CreateStmtDassign(Sym->GetStIdx(), 0, Init);
            Assign->SetSrcPos(sourceLocation2Mpl(DS->getBeginLoc()));
            Res.appendStmtBefore(Assign);
          }
        }
      }
    } else if (llvm::isa<clang::TypedefDecl>(D)) {
      // Safely ignore typedefs
      return Res;
    } else {
      LogInfo::MapleLogger() << "Warning: Non-VarDecl in DeclStmt\n";
      D->dump();
    }
  }

  return Res;
}

Result Clang2MapleVisitor::VisitDoStmt(const clang::DoStmt *Do) {
  WhileStmtNode *WhileNode =
      Builder->GetCurrentFuncCodeMp()->New<WhileStmtNode>(OP_dowhile);
  Result Res(WhileNode, Do->getID(*Context), Do->getDoLoc());

  Result Cond = Visit(Do->getCond());
  WhileNode->SetRHS(exprToCond(getNodeAsRVal(Cond)));

  LabelIdx EndLabel =
      Builder->GetOrCreateMIRLabel("L_END" + std::to_string(++UnnamedCount));
  LabelIdx ExitLabel =
      Builder->GetOrCreateMIRLabel("L_EXIT" + std::to_string(++UnnamedCount));
  EndLabels.push(LabelInfo(EndLabel));
  ExitLabels.push(LabelInfo(ExitLabel));

  const clang::Stmt *BodyStmt = Do->getBody();
  Result BodyRes = Visit(BodyStmt);
  if (llvm::isa<clang::CompoundStmt>(BodyStmt)) {
    WhileNode->SetBody(static_cast<BlockNode *>(BodyRes.getNode()));
  } else {
    BlockNode *Block = Builder->GetCurrentFuncCodeMp()->New<BlockNode>();
    addToBlock(Block, BodyRes);
    WhileNode->SetBody(Block);
  }

  // Add an end label, if needed, to the end of the body
  LabelInfo End = EndLabels.top();
  if (End.Used) {
    StmtNode *EndLabelStmt = Builder->CreateStmtLabel(End.Label);
    EndLabelStmt->SetSrcPos(sourceLocation2Mpl(Do->getEndLoc()));
    WhileNode->GetBody()->AddStatement(EndLabelStmt);
  }
  EndLabels.pop();

  // The condition is called on every iteration of the loop, so if there are any
  // statements that must run before the condition expression, insert them at
  // the end of the loop.
  Cond = Visit(Do->getCond());
  getNodeAsRVal(Cond);
  for (Result::iterator It = Cond.beginStmtsBefore();
       It != Cond.endStmtsBefore(); ++It) {
    WhileNode->GetBody()->AddStatement(*It);
  }

  // Add an exit label, if needed, after this loop
  LabelInfo Exit = ExitLabels.top();
  if (Exit.Used) {
    StmtNode *ExitLabelStmt = Builder->CreateStmtLabel(Exit.Label);
    ExitLabelStmt->SetSrcPos(sourceLocation2Mpl(Do->getEndLoc()));
    Res.appendStmtAfter(ExitLabelStmt);
  }
  ExitLabels.pop();

  return Res;
}

Result Clang2MapleVisitor::VisitForStmt(const clang::ForStmt *For) {
  WhileStmtNode *WhileNode =
      Builder->GetCurrentFuncCodeMp()->New<WhileStmtNode>(OP_while);
  Result Res(WhileNode, For->getID(*Context), For->getForLoc());

  const clang::Stmt *InitStmt = For->getInit();
  const clang::Expr *CondExpr = For->getCond();
  const clang::Expr *IncExpr = For->getInc();

  if (InitStmt) {
    Result Init = Visit(InitStmt);
    Res.appendStmts(Init);
    if (!Init.mayDrop()) {
      StmtNode *SNode = getNodeAsStmt(Init);
      Res.appendStmtBefore(SNode);
    }
  }

  Result Cond(CondExpr ? CondExpr->getBeginLoc() : For->getForLoc());
  if (CondExpr) {
    Cond = Visit(CondExpr);
    WhileNode->SetRHS(exprToCond(getNodeAsRVal(Cond)));
    Res.appendStmts(Cond);
  } else {
    // If condition is null, create a constant 1 for the condition.
    WhileNode->SetRHS(Builder->CreateIntConst(1, PTY_i32));
  }

  LabelIdx EndLabel =
      Builder->GetOrCreateMIRLabel("L_END" + std::to_string(++UnnamedCount));
  LabelIdx ExitLabel =
      Builder->GetOrCreateMIRLabel("L_EXIT" + std::to_string(++UnnamedCount));
  EndLabels.push(LabelInfo(EndLabel));
  ExitLabels.push(LabelInfo(ExitLabel));

  const clang::Stmt *BodyStmt = For->getBody();
  Result BodyRes = Visit(BodyStmt);
  if (llvm::isa<clang::CompoundStmt>(BodyStmt)) {
    WhileNode->SetBody(static_cast<BlockNode *>(BodyRes.getNode()));
  } else {
    BlockNode *Block = Builder->GetCurrentFuncCodeMp()->New<BlockNode>();
    addToBlock(Block, BodyRes);
    WhileNode->SetBody(Block);
  }

  // Add an end label, if needed, to the end of the body
  LabelInfo End = EndLabels.top();
  if (End.Used) {
    StmtNode *EndLabelStmt = Builder->CreateStmtLabel(End.Label);
    EndLabelStmt->SetSrcPos(sourceLocation2Mpl(For->getEndLoc()));
    WhileNode->GetBody()->AddStatement(EndLabelStmt);
  }
  EndLabels.pop();

  if (IncExpr) {
    Result Inc = Visit(IncExpr);
    addToBlock(WhileNode->GetBody(), Inc);
  }

  // The condition is called on every iteration of the loop, so if there are any
  // statements that must run before the condition expression, insert them at
  // the end of the loop.
  if (CondExpr) {
    Cond = Visit(CondExpr);
    getNodeAsRVal(Cond);
    for (Result::iterator It = Cond.beginStmtsBefore();
         It != Cond.endStmtsBefore(); ++It) {
      WhileNode->GetBody()->AddStatement(*It);
    }
  }

  // Add an exit label, if needed, after this loop
  LabelInfo Exit = ExitLabels.top();
  if (Exit.Used) {
    StmtNode *ExitLabelStmt = Builder->CreateStmtLabel(Exit.Label);
    ExitLabelStmt->SetSrcPos(sourceLocation2Mpl(For->getEndLoc()));
    Res.appendStmtAfter(ExitLabelStmt);
  }
  ExitLabels.pop();

  return Res;
}

Result Clang2MapleVisitor::VisitIfStmt(const clang::IfStmt *If) {
  Result Cond = Visit(If->getCond());
  IfStmtNode *IfNode = Builder->CreateStmtIf(exprToCond(getNodeAsRVal(Cond)));
  Result Res(IfNode, If->getID(*Context), If->getBeginLoc());
  Res.appendStmts(Cond);

  const clang::Stmt *ThenStmt = If->getThen();
  Result Then = Visit(ThenStmt);

  if (llvm::isa<clang::CompoundStmt>(ThenStmt)) {
    IfNode->SetThenPart(static_cast<BlockNode *>(Then.getNode()));
  } else {
    BlockNode *Block = Builder->GetCurrentFuncCodeMp()->New<BlockNode>();
    addToBlock(Block, Then);
    IfNode->SetThenPart(Block);
  }

  const clang::Stmt *ElseStmt = If->getElse();
  if (ElseStmt) {
    Result Else = Visit(ElseStmt);

    if (llvm::isa<clang::CompoundStmt>(ElseStmt)) {
      IfNode->SetElsePart(static_cast<BlockNode *>(Else.getNode()));
    } else {
      BlockNode *Block = Builder->GetCurrentFuncCodeMp()->New<BlockNode>();
      addToBlock(Block, Else);
      IfNode->SetElsePart(Block);
    }
  }

  return Res;
}

Result Clang2MapleVisitor::VisitNullStmt(const clang::NullStmt *N) {
  return Result(N->getBeginLoc());
}

Result Clang2MapleVisitor::VisitReturnStmt(const clang::ReturnStmt *Return) {
  Result RetValRes(Return->getReturnLoc());
  Result Res(Return->getReturnLoc());
  BaseNode *RetVal = nullptr;
  if (const clang::Expr *E = Return->getRetValue()) {
    RetValRes = Visit(E);
    Res.appendStmts(RetValRes);
    if (E->getType()->isVoidType()) {
      Res.appendStmtBefore(getNodeAsStmt(RetValRes));
    } else {
      RetVal = getNodeAsRVal(RetValRes);
      Res.appendStmts(RetValRes);
    }
  } else if (Builder->GetCurrentFunction()->GetReturnType()->GetPrimType() !=
             PTY_void) {
    // Handle a return statement with no expression in a function that has a
    // non-void return type.
    RetVal = Builder->CreateConstval(
        createZero(Builder->GetCurrentFunction()->GetReturnType()));
  }

  StmtNode *Ret = nullptr;
  // If the address of a return structure has been passed as an implicit
  // parameter, write the return value to it, otherwise, generate a return
  // statement.
  if (Builder->GetCurrentFunction()->IsFirstArgReturn()) {
    FormalDef RetAddr = Builder->GetCurrentFunction()->GetFormalDefAt(0);
    MIRType *RetTy =
        GlobalTables::GetTypeTable().GetTypeFromTyIdx(RetAddr.formalTyIdx);
    StmtNode *Assign = Builder->CreateStmtIassign(
        *RetTy, 0, Builder->CreateExprDread(*RetTy, *RetAddr.formalSym),
        RetVal);
    Assign->SetSrcPos(sourceLocation2Mpl(Return->getReturnLoc()));
    Res.appendStmtBefore(Assign);
    Ret = Builder->CreateStmtReturn(nullptr);
  } else {
    Ret = Builder->CreateStmtReturn(RetVal);
  }

  Res.setNode(Ret, Return->getID(*Context));
  return Res;
}

Result Clang2MapleVisitor::VisitWhileStmt(const clang::WhileStmt *While) {
  WhileStmtNode *WhileNode =
      Builder->GetCurrentFuncCodeMp()->New<WhileStmtNode>(OP_while);
  Result Res(WhileNode, While->getID(*Context), While->getWhileLoc());

  Result Cond = Visit(While->getCond());
  WhileNode->SetRHS(exprToCond(getNodeAsRVal(Cond)));
  Res.appendStmts(Cond);

  LabelIdx EndLabel =
      Builder->GetOrCreateMIRLabel("L_END" + std::to_string(++UnnamedCount));
  LabelIdx ExitLabel =
      Builder->GetOrCreateMIRLabel("L_EXIT" + std::to_string(++UnnamedCount));
  EndLabels.push(LabelInfo(EndLabel));
  ExitLabels.push(LabelInfo(ExitLabel));

  const clang::Stmt *BodyStmt = While->getBody();
  Result BodyRes = Visit(BodyStmt);
  if (llvm::isa<clang::CompoundStmt>(BodyStmt)) {
    WhileNode->SetBody(static_cast<BlockNode *>(BodyRes.getNode()));
  } else {
    BlockNode *Block = Builder->GetCurrentFuncCodeMp()->New<BlockNode>();
    addToBlock(Block, BodyRes);
    WhileNode->SetBody(Block);
  }

  // Add an end label, if needed, to the end of the body
  LabelInfo End = EndLabels.top();
  if (End.Used) {
    StmtNode *EndLabelStmt = Builder->CreateStmtLabel(End.Label);
    EndLabelStmt->SetSrcPos(sourceLocation2Mpl(While->getEndLoc()));
    WhileNode->GetBody()->AddStatement(EndLabelStmt);
  }
  EndLabels.pop();

  // The condition is called on every iteration of the loop, so if there are any
  // statements that must run before the condition expression, insert them at
  // the end of the loop.
  Cond = Visit(While->getCond());
  getNodeAsRVal(Cond);
  for (Result::iterator It = Cond.beginStmtsBefore();
       It != Cond.endStmtsBefore(); ++It) {
    WhileNode->GetBody()->AddStatement(*It);
  }

  // Add an exit label, if needed, after this loop
  LabelInfo Exit = ExitLabels.top();
  if (Exit.Used) {
    StmtNode *ExitLabelStmt = Builder->CreateStmtLabel(Exit.Label);
    ExitLabelStmt->SetSrcPos(sourceLocation2Mpl(While->getEndLoc()));
    Res.appendStmtAfter(ExitLabelStmt);
  }
  ExitLabels.pop();

  return Res;
}

Result Clang2MapleVisitor::VisitBreakStmt(const clang::BreakStmt *BS) {
  ASSERT(ExitLabels.size() > 0, "'break' outside of known loop or switch");
  LabelInfo &LI = ExitLabels.top();
  LI.Used = true;
  GotoNode *GoTo = Builder->CreateStmtGoto(OP_goto, LI.Label);
  return Result(GoTo, BS->getID(*Context), BS->getBreakLoc());
}

Result Clang2MapleVisitor::VisitContinueStmt(const clang::ContinueStmt *CS) {
  ASSERT(EndLabels.size() > 0, "'continue' outside of known loop");
  LabelInfo &LI = EndLabels.top();
  LI.Used = true;
  GotoNode *GoTo = Builder->CreateStmtGoto(OP_goto, LI.Label);
  return Result(GoTo, CS->getID(*Context), CS->getContinueLoc());
}

Result Clang2MapleVisitor::VisitLabelStmt(const clang::LabelStmt *LS) {
  LabelIdx Label = Builder->GetOrCreateMIRLabel(LS->getName());
  StmtNode *LabelStmt = Builder->CreateStmtLabel(Label);
  LabelStmt->SetSrcPos(sourceLocation2Mpl(LS->getIdentLoc()));

  Result SubExprRes = Visit(LS->getSubStmt());
  SubExprRes.prependStmtBefore(LabelStmt);
  return SubExprRes;
}

Result Clang2MapleVisitor::VisitGotoStmt(const clang::GotoStmt *Goto) {
  LabelIdx Label =
      Builder->GetOrCreateMIRLabel(Goto->getLabel()->getName().str());
  StmtNode *GotoStmt = Builder->CreateStmtGoto(OP_goto, Label);
  return Result(GotoStmt, Goto->getID(*Context), Goto->getGotoLoc());
}

Result
Clang2MapleVisitor::VisitIndirectGotoStmt(const clang::IndirectGotoStmt *Goto) {
  Result TargetRes = Visit(Goto->getTarget());
  BaseNode *Target = getNodeAsRVal(TargetRes);
  StmtNode *GotoStmt = Builder->GetCurrentFuncCodeMp()->New<UnaryStmtNode>(
      OP_igoto, PointerPrimTy, Target);
  Result Res(GotoStmt, Goto->getID(*Context), Goto->getGotoLoc());
  Res.appendStmts(TargetRes);
  return Res;
}

Result Clang2MapleVisitor::VisitSwitchStmt(const clang::SwitchStmt *Switch) {
  Result CondRes = Visit(Switch->getCond());

  CaseVector *Cases = Builder->GetCurrentFuncCodeMp()->New<CaseVector>(
      Builder->GetCurrentFuncCodeMpAllocator()->Adapter());
  SwitchCases.push(Cases);

  LabelIdx DefaultLabel = Builder->GetOrCreateMIRLabel(
      "L_DEFAULT" + std::to_string(++UnnamedCount));
  LabelIdx BreakLabel =
      Builder->GetOrCreateMIRLabel("L_BREAK" + std::to_string(++UnnamedCount));
  DefaultLabels.push(LabelInfo(DefaultLabel));
  ExitLabels.push(LabelInfo(BreakLabel));

  const clang::Stmt *BodyStmt = Switch->getBody();
  Result BodyRes = Visit(BodyStmt);

  // Generate a temporary for the switch condition
  MIRSymbol *Temp = Builder->GetOrCreateLocalDecl(
      "_switch." + std::to_string(Switch->getID(*Context)),
      *CondRes.getValueTy());
  StmtNode *TempAssign =
      Builder->CreateStmtDassign(*Temp, 0, getNodeAsRVal(CondRes));
  TempAssign->SetSrcPos(sourceLocation2Mpl(CondRes.getLoc()));
  BaseNode *ReadCond = Builder->CreateExprDread(*CondRes.getValueTy(), *Temp);

  SwitchNode *SwitchStmt =
      Builder->CreateStmtSwitch(ReadCond, DefaultLabel, *Cases);
  Result Res(SwitchStmt, Switch->getID(*Context), Switch->getSwitchLoc());

  // Add statements that need to run before the switch, from the condition.
  for (Result::iterator It = CondRes.beginStmtsBefore();
       It != CondRes.endStmtsBefore(); ++It) {
    Res.appendStmtBefore(*It);
  }

  Res.appendStmtBefore(TempAssign);

  // Add the body after the switch.
  if (llvm::isa<clang::CompoundStmt>(BodyStmt)) {
    BlockNode *BodyBlock = static_cast<BlockNode *>(BodyRes.getNode());
    for (auto &Stmt : BodyBlock->GetStmtNodes()) {
      Res.appendStmtAfter(&Stmt);
    }
  } else {
    StmtNode *SNode = getNodeAsStmt(BodyRes);
    Res.appendStmtAfter(SNode);
  }
  Res.appendStmts(BodyRes);

  // If there was no default case, add the label.
  LabelInfo &DefaultLI = DefaultLabels.top();
  if (!DefaultLI.Used) {
    StmtNode *DefaultStmt = Builder->CreateStmtLabel(DefaultLI.Label);
    DefaultStmt->SetSrcPos(sourceLocation2Mpl(Switch->getSwitchLoc()));
    Res.appendStmtAfter(DefaultStmt);
  }

  // If there were any breaks in the body, add the label for the exit.
  LabelInfo &ExitLI = ExitLabels.top();
  if (ExitLI.Used) {
    StmtNode *ExitLabelStmt = Builder->CreateStmtLabel(ExitLI.Label);
    ExitLabelStmt->SetSrcPos(sourceLocation2Mpl(Switch->getEndLoc()));
    Res.appendStmtAfter(ExitLabelStmt);
  }

  SwitchCases.pop();
  DefaultLabels.pop();
  ExitLabels.pop();

  return Res;
}

Result Clang2MapleVisitor::VisitCaseStmt(const clang::CaseStmt *CS) {
  LabelIdx Label =
      Builder->GetOrCreateMIRLabel("L_CASE" + std::to_string(++UnnamedCount));
  StmtNode *LabelStmt = Builder->CreateStmtLabel(Label);
  LabelStmt->SetSrcPos(sourceLocation2Mpl(CS->getCaseLoc()));

  clang::Expr::EvalResult Constant;
  bool IsConstant = CS->getLHS()->EvaluateAsConstantExpr(Constant, *Context);
  ASSERT(IsConstant, "case statement expression is not constant");
  if (!Constant.Val.isInt()) {
    LogInfo::MapleLogger() << "Error: Constant.Val in case statement is "
                           << Constant.Val.getKind() << "\n";
    Constant.Val.dump();
  }
  llvm::APSInt Value = Constant.Val.getInt();

  ASSERT(SwitchCases.size() > 0, "'case' outside of known switch");
  CaseVector *Cases = SwitchCases.top();
  Cases->push_back(CasePair(Value.getExtValue(), Label));

  Result Body = Visit(CS->getSubStmt());
  Body.prependStmtBefore(LabelStmt);
  return Body;
}

Result Clang2MapleVisitor::VisitDefaultStmt(const clang::DefaultStmt *DS) {
  ASSERT(DefaultLabels.size() > 0, "'default' outside of known switch");
  LabelInfo &LI = DefaultLabels.top();
  StmtNode *DefaultStmt = Builder->CreateStmtLabel(LI.Label);
  DefaultStmt->SetSrcPos(sourceLocation2Mpl(DS->getDefaultLoc()));

  LI.Used = true;

  Result Body = Visit(DS->getSubStmt());
  Body.prependStmtBefore(DefaultStmt);
  return Body;
}

//
// Visit Expressions
//

Result Clang2MapleVisitor::Visit(const clang::Stmt *S) {
  if (const clang::Expr *E = clang::dyn_cast<clang::Expr>(S)) {
    MIRType *ResTy = type2Mpl(E->getType());
    if (MIRConst *Const = evaluateExprAsConst(E, ResTy)) {
      // If this is not in an initializer, set the type to a register-sized
      // type.
      if (!Initializer && Const->GetKind() == kConstInt) {
        MIRIntConst *IConst = static_cast<MIRIntConst *>(Const);
        PrimType PTy = Const->GetType().GetPrimType();
        PrimType RegPTy = GetRegPrimType(PTy);
        if (RegPTy != PTy) {
          Const = GlobalTables::GetIntConstTable().GetOrCreateIntConst(
              IsSignedInteger(PTy) ? IConst->GetValue().GetSXTValue() : IConst->GetValue().GetZXTValue(),
              *GlobalTables::GetTypeTable().GetPrimType(RegPTy));
        }
      }
      BaseNode *N = Builder->CreateConstval(Const);
      Result Res(N, E->getID(*Context), E->getBeginLoc(), ResTy);
      return Res;
    }
  }
  return clang::ConstStmtVisitor<Clang2MapleVisitor, Result>::Visit(S);
}

Result Clang2MapleVisitor::VisitAddrLabelExpr(const clang::AddrLabelExpr *ALE) {
  LabelIdx Label =
      Builder->GetOrCreateMIRLabel(ALE->getLabel()->getName().str());
  AddroflabelNode *AOL =
      Builder->GetCurrentFuncCodeMp()->New<AddroflabelNode>(Label);
  AOL->SetPrimType(PointerPrimTy);
  return Result(AOL, ALE->getID(*Context), ALE->getAmpAmpLoc(),
                type2Mpl(ALE->getType()));
}

Result Clang2MapleVisitor::VisitArraySubscriptExpr(
    const clang::ArraySubscriptExpr *ASE) {
  MIRType *Ty = type2Mpl(ASE->getType());
  Result BaseRes = Visit(ASE->getBase());
  Result IdxRes = Visit(ASE->getIdx());
  BaseNode *Idx = getNodeAsRVal(IdxRes);

  MIRType *IdxTy = GlobalTables::GetTypeTable().GetPrimType(
      GetRegPrimType(Idx->GetPrimType()));
  Idx = createNumericCast(IdxTy, Idx);

  ArrayNode *Array;
#ifdef MULTIDIM_ARRAYS
  // If this is a multi-dimensional array subscript (ex. x[3][4]), append to the
  // existing array node. Do not use multi-dimensional array subscripting when
  // accessing through a pointer, or when accessing a field of the array
  // element.
  BaseNode *ArrBase = BaseRes.getNode();
  if (ArrBase && ArrBase->GetOpCode() == OP_array &&
      BaseRes.getValueTy()->GetKind() == kTypeArray &&
      BaseRes.getField() == 0) {
    Array = static_cast<ArrayNode *>(ArrBase);
    MapleVector<BaseNode *> &Opnds = Array->GetNopnd();
    Opnds.push_back(Idx);
    Array->SetNumOpnds(Opnds.size());
  } else {
#endif // MULTIDIM_ARRAYS
    // Get the base array type from the base expression, ignoring the implicit
    // array-to-pointer decay cast
    MIRType *ArrayTy = type2Mpl(ASE->getBase()->IgnoreImpCasts()->getType());
    if (ArrayTy->GetKind() == kTypePointer) {
      MIRPtrType *PointerTy = static_cast<MIRPtrType *>(ArrayTy);
      MIRType *PointeeTy = PointerTy->GetPointedType();
#ifdef MULTIDIM_ARRAYS
      if (PointeeTy->GetKind() == kTypeArray) {
        MIRArrayType *PointeeArrTy = static_cast<MIRArrayType *>(PointeeTy);
        std::vector<maple::uint32> SizeArray{1};
        for (size_t d = 0; d < PointeeArrTy->GetDim(); d++) {
          SizeArray.push_back(PointeeArrTy->GetSizeArrayItem(d));
        }
        MIRArrayType NewArrTy(PointeeArrTy->GetElemTyIdx(), SizeArray);
        ArrayTy = static_cast<MIRArrayType *>(
            GlobalTables::GetTypeTable().GetOrCreateMIRTypeNode(NewArrTy));
      } else {
#endif // MULTIDIM_ARRAYS
        ArrayTy =
            GlobalTables::GetTypeTable().GetOrCreateArrayType(*PointeeTy, 1);
#ifdef MULTIDIM_ARRAYS
      }
#endif // MULTIDIM_ARRAYS
    }

    // If this is an access of a 0-size array, change the type to be a 1-size
    // array.
    if (ArrayTy->GetKind() == kTypeArray) {
      MIRArrayType *ArrTy = static_cast<MIRArrayType *>(ArrayTy);
      if (ArrTy->GetSizeArrayItem(0) == 0) {
        std::vector<maple::uint32> SizeArray{1};
        for (size_t d = 1; d < ArrTy->GetDim(); d++) {
          SizeArray.push_back(ArrTy->GetSizeArrayItem(d));
        }
        MIRArrayType NewArrTy(ArrTy->GetElemTyIdx(), SizeArray);
        ArrayTy = static_cast<MIRArrayType *>(
            GlobalTables::GetTypeTable().GetOrCreateMIRTypeNode(NewArrTy));
      }
    }
    Array = Builder->CreateExprArray(*ArrayTy, getNodeAsRVal(BaseRes), Idx);
    Array->SetBoundsCheck(false);
    Array->SetPrimType(PointerPrimTy);
#ifdef MULTIDIM_ARRAYS
  }
#endif // MULTIDIM_ARRAYS
  Result Res(Array, ASE->getID(*Context), type2Mpl(ASE->getBase()->getType()),
             Ty, ASE->getBeginLoc());
  Res.appendStmts(BaseRes);
  Res.appendStmts(IdxRes);
  return Res;
}

int Clang2MapleVisitor::typeRank(PrimType PTy) {
  switch (PTy) {
  case PTY_i8:
  case PTY_i16:
  case PTY_i32:
    return 0;
  case PTY_u1:
  case PTY_u8:
  case PTY_u16:
  case PTY_u32:
    return 1;
  case PTY_a32:
    return 2;
  case PTY_i64:
    return 3;
  case PTY_u64:
    return 4;
  case PTY_a64:
    return 5;
  case PTY_ptr:
  case PTY_ref:
    return PointerPrimTy == PTY_a32 ? 2 : 4;
  case PTY_f32:
    return 6;
  case PTY_f64:
    return 7;
  case PTY_f128:
    return 8;
  default:
    return -1;
  }
}

PrimType Clang2MapleVisitor::typeOfOperation(PrimType PTy1, PrimType PTy2) {
  static PrimType RankPrimTy[] = {PTY_i32, PTY_u32, PTY_a32, PTY_i64, PTY_u64,
                                  PTY_a64, PTY_f32, PTY_f64, PTY_f128};
  int R1, R2, Rank;
  R1 = typeRank(PTy1);
  R2 = typeRank(PTy2);
  Rank = std::max(R1, R2);
  if (Rank < 0)
    return PTy1;
  return RankPrimTy[Rank];
}

Result
Clang2MapleVisitor::VisitBinaryOperator(const clang::BinaryOperator *BO) {
  Result Lhs = Visit(BO->getLHS());
  Result Rhs = Visit(BO->getRHS());
  MIRType *Ty = type2Mpl(BO->getType());
  StmtNode *Assign = nullptr;

  // Assignments need to be handled differently from the other operators
  if (BO->isAssignmentOp()) {
    Result Res(BO->getExprLoc());
    BaseNode *RhsNode = getNodeAsRVal(Rhs);
    bool CompoundAssign = false;

    // For compound assignments, convert the RHS to LHS op RHS
    if (auto CAO = llvm::dyn_cast<clang::CompoundAssignOperator>(BO)) {
      CompoundAssign = true;
      bool isSigned = BO->getType()->isSignedIntegerType();
      Opcode Op = BinOpcode2Mpl(BO->getOpcode(), isSigned);
      if (Op == OP_undef) {
        LogInfo::MapleLogger()
            << "Error: Unhandled opcode in binary operator: " << BO->getOpcode()
            << "\n";
        CHECK_FATAL(false, "Failing due to unsupported feature");
      }

      MIRType *ResultTy = type2Mpl(CAO->getComputationResultType());
      MIRType *LhsTy = type2Mpl(CAO->getComputationLHSType());
      BaseNode *LhsOp = getNodeAsRVal(Lhs);
      if (Lhs.getValueTy() != LhsTy) {
        LhsOp = createNumericCast(LhsTy, LhsOp);
      }

      // If this is addition or subtraction of a pointer and an integer, the
      // integer must be scaled to the size of the pointed-to type.
      if (Op == OP_add || Op == OP_sub) {
        if (isPointerType(LhsTy) && !isPointerType(Rhs.getValueTy())) {
          size_t Scale = getPointedToSize(LhsTy);
          if (Scale > 1) { // Skip multiplication for 1 (char) and 0 (void)
            if (RhsNode->GetOpCode() == OP_constval) {
              ConstvalNode *RhsConst = static_cast<ConstvalNode *>(RhsNode);
              ASSERT(RhsConst->GetConstVal()->GetKind() == kConstInt,
                     "expected int const");
              MIRIntConst *IntConst =
                  static_cast<MIRIntConst *>(RhsConst->GetConstVal());
              int64_t Val = IntConst->GetValue().GetSXTValue();
              RhsNode = Builder->CreateIntConst(Val * Scale, PointerPrimTy);
            } else {
              ConstvalNode *Const =
                  Builder->CreateIntConst(Scale, PointerPrimTy);
              RhsNode = Builder->CreateExprBinary(
                  OP_mul, *IntPointerTy,
                  createNumericCast(IntPointerTy, RhsNode), Const);
            }
          }
        }
      }

      RhsNode = Builder->CreateExprBinary(Op, *ResultTy, LhsOp,
                                          createNumericCast(ResultTy, RhsNode));
      if (ResultTy != Ty) {
        RhsNode = createNumericCast(Ty, RhsNode);
      }
    }

    BaseNode *N;
    if (Lhs.isDeref()) {
      Assign = Builder->CreateStmtIassign(*Lhs.getAddrTy(), Lhs.getField(),
                                          Lhs.getAddr(), RhsNode);
      if (CompoundAssign) {
        N = Builder->CreateExprIread(*Lhs.getValueTy(), *Lhs.getAddrTy(),
                                     Lhs.getField(), Lhs.getAddr());
      }
    } else if (Lhs.isSym()) {
      Assign =
          Builder->CreateStmtDassign(*Lhs.getSym(), Lhs.getField(), RhsNode);
      if (CompoundAssign) {
        N = Builder->CreateExprDread(*Lhs.getValueTy(), Lhs.getField(),
                                     *Lhs.getSym());
      }
    } else {
      BO->getLHS()->dump();
      ASSERT(false, "Error: LHS of assign is neither deref or symbol");
    }

    Res.appendStmts(Rhs);
    Res.appendStmts(Lhs);
    if (CompoundAssign) {
      Assign->SetSrcPos(sourceLocation2Mpl(BO->getExprLoc()));
      Res.appendStmtBefore(Assign);
      Res.setNode(N, BO->getID(*Context), Lhs.getValueTy(), TypeAttrs(), true);
    } else {
      Res.setNode(Assign, BO->getID(*Context), Ty);
    }
    return Res;
  }

  // Comma operator is also unique
  if (BO->getOpcode() == clang::BO_Comma) {
    Result Res(BO->getRHS()->getBeginLoc());
    appendResultBefore(Res, Lhs);
    Res.appendStmts(Rhs);
    Res.setResult(Rhs);
    return Res;
  }

  BaseNode *RhsNode = getNodeAsRVal(Rhs);
  BaseNode *LhsNode = getNodeAsRVal(Lhs);

  // For logical and/or, turn both left and right hand sides into conditional
  // statements if needed.
  if (BO->getOpcode() == clang::BO_LAnd || BO->getOpcode() == clang::BO_LOr) {
    LhsNode = exprToCond(LhsNode);
    RhsNode = exprToCond(RhsNode);
  }
  // For many binary operations, the types of the two operands must match. Cast
  // the operands, if needed, to the appropriate type for the operation.
  else if ((BO->getOpcode() >= clang::BO_Mul &&
            BO->getOpcode() <= clang::BO_Sub) ||
           (BO->getOpcode() >= clang::BO_LT &&
            BO->getOpcode() <= clang::BO_LOr)) {
    MIRType *OpTy = GlobalTables::GetTypeTable().GetPrimType(
        typeOfOperation(LhsNode->GetPrimType(), RhsNode->GetPrimType()));
    RhsNode = createNumericCast(OpTy, RhsNode);
    LhsNode = createNumericCast(OpTy, LhsNode);
  }

  BaseNode *BinNode;
  bool isSigned = BO->getType()->isSignedIntegerType();
  Opcode Op = BinOpcode2Mpl(BO->getOpcode(), isSigned);
  if (Op == OP_undef) {
    LogInfo::MapleLogger() << "Error: Unhandled opcode in binary operator: "
                           << BO->getOpcode() << "\n";
    CHECK_FATAL(false, "Failing due to unsupported feature");
  }

  if ((Op == OP_cior || Op == OP_cand) &&
      !(Rhs.isSimple() && useSimpleShortCircuit())) {
    // Handle C's short-circuiting rules:
    //   x || y;
    // becomes:
    //   _shortcircuit.0 = x;
    //  if (_shortcircuit.0) goto L0;
    //  _shortcircuit.0 = y;
    //  L0: _shortcircuit.0
    Result Res(BO->getExprLoc());
    MIRType *IntTy =
        GlobalTables::GetTypeTable().GetTypeFromTyIdx(TyIdx(PTY_i32));
    // The label should be recreated if this node is visited multiple times, so
    // use an iterative name.
    LabelIdx LabelID = Builder->GetOrCreateMIRLabel(
        "Lshortcircuit." + std::to_string(++UnnamedCount));
    // The temporary variable should be reused if this node is visited multiple
    // times, so use a stable name.
    MIRSymbol *Temp = Builder->GetOrCreateLocalDecl(
        "_shortcircuit." + std::to_string(BO->getID(*Context)), *IntTy);

    Res.appendStmts(Lhs);
    StmtNode *TempAssign =
        Builder->CreateStmtDassign(*Temp, 0, exprToCond(LhsNode));
    TempAssign->SetSrcPos(sourceLocation2Mpl(Lhs.getLoc()));
    Res.appendStmtBefore(TempAssign);

    CondGotoNode *Branch = Builder->CreateStmtCondGoto(
        Builder->CreateExprDread(*IntTy, *Temp),
        Op == OP_cior ? OP_brtrue : OP_brfalse, LabelID);
    Branch->SetSrcPos(sourceLocation2Mpl(BO->getOperatorLoc()));
    Res.appendStmtBefore(Branch);

    Res.appendStmts(Rhs);
    TempAssign = Builder->CreateStmtDassign(*Temp, 0, exprToCond(RhsNode));
    TempAssign->SetSrcPos(sourceLocation2Mpl(Rhs.getLoc()));
    Res.appendStmtBefore(TempAssign);

    StmtNode *LabelStmt = Builder->CreateStmtLabel(LabelID);
    LabelStmt->SetSrcPos(sourceLocation2Mpl(BO->getOperatorLoc()));
    Res.appendStmtBefore(LabelStmt);

    Res.setSym(Temp, IntTy, true);
    return Res;
  } else if (kOpcodeInfo.IsCompare(Op)) {
    // Compare operators need two types
    MIRType *OpndTy = type2Mpl(BO->getLHS()->getType());
    BinNode = Builder->CreateExprCompare(Op, *Ty, *OpndTy, LhsNode, RhsNode);
  } else {
    MIRType *LhsTy = Lhs.getValueTy();
    MIRType *RhsTy = Rhs.getValueTy();

    // If this is addition or subtraction of a pointer and an integer, the
    // integer must be scaled to the size of the pointed-to type.
    if (Op == OP_add || Op == OP_sub) {
      if (!isPointerType(LhsTy) && isPointerType(RhsTy)) {
        size_t Scale = getPointedToSize(RhsTy);
        if (Scale > 1) { // Skip multiplication for 1 (char) and 0 (void)
          if (LhsNode->GetOpCode() == OP_constval) {
            ConstvalNode *LhsConst = static_cast<ConstvalNode *>(LhsNode);
            ASSERT(LhsConst->GetConstVal()->GetKind() == kConstInt,
                   "expected int const");
            MIRIntConst *IntConst =
                static_cast<MIRIntConst *>(LhsConst->GetConstVal());
            int64_t Val = IntConst->GetValue().GetSXTValue();
            LhsNode = Builder->CreateIntConst(Val * Scale, PointerPrimTy);
          } else {
            ConstvalNode *Const = Builder->CreateIntConst(Scale, PointerPrimTy);
            LhsNode = Builder->CreateExprBinary(OP_mul, *IntPointerTy, LhsNode,
                                                Const);
          }
        }
      } else if (isPointerType(LhsTy) && !isPointerType(RhsTy)) {
        size_t Scale = getPointedToSize(LhsTy);
        if (Scale > 1) { // Skip multiplication for 1 (char) and 0 (void)
          if (RhsNode->GetOpCode() == OP_constval) {
            ConstvalNode *RhsConst = static_cast<ConstvalNode *>(RhsNode);
            ASSERT(RhsConst->GetConstVal()->GetKind() == kConstInt,
                   "expected int const");
            MIRIntConst *IntConst =
                static_cast<MIRIntConst *>(RhsConst->GetConstVal());
            int64_t Val = IntConst->GetValue().GetSXTValue();
            RhsNode = Builder->CreateIntConst(Val * Scale, PointerPrimTy);
          } else {
            ConstvalNode *Const = Builder->CreateIntConst(Scale, PointerPrimTy);
            RhsNode = Builder->CreateExprBinary(OP_mul, *IntPointerTy, RhsNode,
                                                Const);
          }
        }
      }
    }

    BinNode = Builder->CreateExprBinary(Op, *Ty, LhsNode, RhsNode);

    // For the difference of two pointers, the result needs to be scaled by the
    // size of the pointed-to object.
    if (Op == OP_sub && isPointerType(LhsTy) && isPointerType(RhsTy)) {
      size_t Scale = getPointedToSize(LhsTy);
      if (Scale > 1) { // Skip multiplication for 1 (char) and 0 (void)
        ConstvalNode *Const = Builder->CreateIntConst(Scale, Ty->GetPrimType());
        BinNode = Builder->CreateExprBinary(OP_div, *Ty, BinNode, Const);
      }
    }
  }

  Result BinRes(BinNode, BO->getID(*Context), BO->getExprLoc(), Ty);
  BinRes.appendStmts(Rhs);
  BinRes.appendStmts(Lhs);
  return BinRes;
}

Result Clang2MapleVisitor::VisitBinaryConditionalOperator(
    const clang::BinaryConditionalOperator *BCO) {
  // First, check if the condition is a compile-time constant
  clang::Expr::EvalResult R;
  bool Success = BCO->getCond()->EvaluateAsInt(R, *Context);
  if (Success) {
    llvm::APSInt Val = R.Val.getInt();
    if (Val.isNullValue()) {
      return Visit(BCO->getFalseExpr());
    } else {
      return Visit(BCO->getCond());
    }
  }

  Result CondRes = Visit(BCO->getCond());
  Result FalseRes = Visit(BCO->getFalseExpr());
  MIRType *Ty = type2Mpl(BCO->getType());

  // In the simple case, both true and false are just one expression
  if (Ty->GetPrimType() != PTY_void && FalseRes.isSimple() &&
      FalseRes.canSpeculate()) {
    Result Res(Builder->CreateExprTernary(
                   OP_select, *Ty, exprToCond(getNodeAsRVal(CondRes)),
                   getNodeAsRVal(CondRes), getNodeAsRVal(FalseRes)),
               BCO->getID(*Context), BCO->getBeginLoc(), Ty);
    Res.appendStmts(CondRes);
    return Res;
  }

  // If not, then expand to an if-then-else
  MIRSymbol *Temp = nullptr;
  if (Ty->GetPrimType() != PTY_void) {
    Temp = Builder->GetOrCreateLocalDecl(
        "cond." + std::to_string(BCO->getID(*Context)), *Ty);
  }
  Result Res(Temp, Ty, BCO->getBeginLoc());

  IfStmtNode *IfNode =
      Builder->CreateStmtIf(exprToNotCond(getNodeAsRVal(CondRes)));
  Res.appendStmts(CondRes);

  BlockNode *Block = Builder->GetCurrentFuncCodeMp()->New<BlockNode>();
  StmtNode *FalsePart;
  if (Temp) {
    FalsePart = Builder->CreateStmtDassign(*Temp, 0, getNodeAsRVal(FalseRes));
  } else {
    FalsePart = getNodeAsStmt(FalseRes);
  }

  for (Result::iterator It = FalseRes.beginStmtsBefore();
       It != FalseRes.endStmtsBefore(); ++It) {
    Block->AddStatement(*It);
  }
  addStmtToBlock(Block, FalsePart);
  for (Result::iterator It = FalseRes.beginStmtsAfter();
       It != FalseRes.endStmtsAfter(); ++It) {
    Block->AddStatement(*It);
  }
  IfNode->SetThenPart(Block);

  IfNode->SetSrcPos(sourceLocation2Mpl(BCO->getBeginLoc()));
  Res.appendStmtBefore(IfNode);
  return Res;
}

Result Clang2MapleVisitor::VisitCallExpr(const clang::CallExpr *CE) {
  MapleVector<BaseNode *> Args(
      Builder->GetCurrentFuncCodeMpAllocator()->Adapter());

  Result Res(CE->getBeginLoc());

  const clang::FunctionDecl *CalleeDecl = CE->getDirectCallee();
  MIRType *Ty = type2Mpl(CE->getType());
  TypeAttrs Attrs;
  if (isOneElementVector(CE->getType())) {
    Attrs.SetAttr(ATTR_oneelem_simd);
  }

  unsigned NumArgs = CE->getNumArgs();
  if (shouldEvaluateArgs(CalleeDecl)) {
    for (unsigned i = 0; i < NumArgs; ++i) {
      Result ArgRes = Visit(CE->getArg(i));
      Args.push_back(getNodeAsRVal(ArgRes));
      Res.appendStmts(ArgRes);
    }
  }

  // Indirect call
  if (!CalleeDecl) {
    Result CalleeRes = Visit(CE->getCallee());

    Args.insert(Args.begin(), getNodeAsRVal(CalleeRes));
    IcallNode *ICall = Builder->CreateStmtIcall(Args);
    Res.appendStmts(CalleeRes);
    Res.setNode(ICall, CE->getID(*Context), Ty, Attrs);
    return Res;
  } else {
    BaseNode *Call;

    // Special cases for some builtins
    unsigned int BuiltinID = CalleeDecl->getBuiltinID();
    // Note: The cases are kept in alphabetical order, ignoring an optional
    // `__builtin_` prefix, and similar builtins are grouped together, even if
    // that puts them out of order.
    switch (CalleeDecl->getBuiltinID()) {
    case clang::Builtin::BI__builtin_abs:
    case clang::Builtin::BI__builtin_fabs:
    case clang::Builtin::BI__builtin_fabsf:
    case clang::Builtin::BI__builtin_fabsl:
    case clang::Builtin::BI__builtin_fabsf16:
    case clang::Builtin::BI__builtin_fabsf128:
    case clang::Builtin::BI__builtin_labs:
    case clang::Builtin::BI__builtin_llabs:
    case clang::Builtin::BIabs:
    case clang::Builtin::BIlabs:
    case clang::Builtin::BIllabs:
    case clang::Builtin::BIfabs:
    case clang::Builtin::BIfabsf:
    case clang::Builtin::BIfabsl:
      Call = Builder->CreateExprUnary(OP_abs, *Ty, Args[0]);
      break;
    case clang::Builtin::BI__builtin_acos:
    case clang::Builtin::BIacos:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_acos, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_acosf:
    case clang::Builtin::BIacosf:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_acosf, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_alloca:
    case clang::Builtin::BI_alloca:
    case clang::Builtin::BIalloca:
      Call = Builder->CreateExprUnary(OP_alloca, *Ty, Args[0]);
      Call->SetPrimType(PointerPrimTy);
      break;
    case clang::Builtin::BI__builtin_asin:
    case clang::Builtin::BIasin:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_asin, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_asinf:
    case clang::Builtin::BIasinf:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_asinf, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_atan:
    case clang::Builtin::BIatan:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_atan, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_atanf:
    case clang::Builtin::BIatanf:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_atanf, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_classify_type: {
      // Let Clang figure out the type classification
      clang::Expr::EvalResult R;
      bool Success = CE->EvaluateAsInt(R, *Context);
      ASSERT(Success, "Failed to evaluate __builtin_classify_type");
      llvm::APSInt Val = R.Val.getInt();
      Call = Builder->CreateIntConst(Val.getExtValue(), Ty->GetPrimType());
    } break;
    case clang::Builtin::BI__builtin_clz:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_clz32, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_clzl:
    case clang::Builtin::BI__builtin_clzll:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_clz64, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_constant_p: {
      int Val = CE->getArg(0)->isConstantInitializer(*Context, false) ? 1 : 0;
      // Pointers are not considered constant
      if (CE->getArg(0)->getType()->isPointerType() &&
          !llvm::isa<clang::StringLiteral>(CE->getArg(0)->IgnoreParenCasts())) {
        Val = 0;
      }
      Call = Builder->CreateIntConst(Val, Ty->GetPrimType());
    } break;
    case clang::Builtin::BI__builtin_cos:
    case clang::Builtin::BIcos:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_cos, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_cosf:
    case clang::Builtin::BIcosf:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_cosf, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_cosh:
    case clang::Builtin::BIcosh:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_cosh, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_coshf:
    case clang::Builtin::BIcoshf:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_coshf, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_ctz:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_ctz32, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_ctzl:
    case clang::Builtin::BI__builtin_ctzll:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_ctz64, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_exp:
    case clang::Builtin::BIexp:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_exp, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_expf:
    case clang::Builtin::BIexpf:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_expf, OP_intrinsicop, *Ty,
                                            Args);
      break;

    case clang::Builtin::BI__builtin_expect: {
      ASSERT(Args.size() == 2, "__builtin_expect requires two arguments");
      // Arg 0 is the expression and arg 1 is the expected value.
      Call = Args[0];
      // TODO: Currently the expected value is just executed, but we should do
      // something with it to improve performance.
      StmtNode *SNode;
      if (kOpcodeInfo.IsStmt(Args[1]->op)) {
        SNode = static_cast<StmtNode *>(Args[1]);
      } else {
        // If the node is an expression, not a statement, wrap it in an eval
        SNode = Builder->CreateStmtUnary(OP_eval, Args[1]);
      }
      Res.appendStmtBefore(SNode);
    } break;
    case clang::Builtin::BI__builtin_ffs:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_ffs, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_fmax:
    case clang::Builtin::BI__builtin_fmaxf:
    case clang::Builtin::BI__builtin_fmaxf16:
    case clang::Builtin::BI__builtin_fmaxl:
    case clang::Builtin::BIfmax:
    case clang::Builtin::BIfmaxf:
    case clang::Builtin::BIfmaxl:
      Call = Builder->CreateExprBinary(OP_max, *Ty, Args[0], Args[1]);
      break;
    case clang::Builtin::BI__builtin_fmin:
    case clang::Builtin::BI__builtin_fminf:
    case clang::Builtin::BI__builtin_fminf16:
    case clang::Builtin::BI__builtin_fminl:
    case clang::Builtin::BIfmin:
    case clang::Builtin::BIfminf:
    case clang::Builtin::BIfminl:
      Call = Builder->CreateExprBinary(OP_min, *Ty, Args[0], Args[1]);
      break;
    case clang::Builtin::BI__builtin_isinf_sign:
      ASSERT(Args.size() == 1, "Incorrect arguments to isinf");
      if (Args[0]->GetPrimType() == PTY_f64) {
        Call = Builder->CreateStmtCall("__isinf", Args);
      } else if (Args[0]->GetPrimType() == PTY_f32) {
        Call = Builder->CreateStmtCall("__isinff", Args);
      } else {
        ASSERT(false, "Unsupported type passed to isinf");
      }
      break;
    case clang::Builtin::BI__builtin_huge_val:
    case clang::Builtin::BI__builtin_inf:
      Call =
          Builder->CreateDoubleConst(std::numeric_limits<double>::infinity());
      break;
    case clang::Builtin::BI__builtin_huge_valf:
    case clang::Builtin::BI__builtin_inff:
      Call = Builder->CreateFloatConst(std::numeric_limits<float>::infinity());
      break;
    case clang::Builtin::BI__builtin_log:
    case clang::Builtin::BIlog:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_log, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_logf:
    case clang::Builtin::BIlogf:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_logf, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_log10:
    case clang::Builtin::BIlog10:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_log10, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_log10f:
    case clang::Builtin::BIlog10f:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_log10f, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_memcmp:
    case clang::Builtin::BImemcmp:
      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_memcmp, Args);
      break;
    case clang::Builtin::BI__builtin_memcpy:
    case clang::Builtin::BImemcpy:
      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_memcpy, Args);
      break;
    case clang::Builtin::BI__builtin_memmove:
    case clang::Builtin::BImemmove:
      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_memmove, Args);
      break;
    case clang::Builtin::BI__builtin_memset:
    case clang::Builtin::BImemset:
      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_memset, Args);
      break;
    case clang::Builtin::BI__builtin_prefetch:
      // TODO: Do something useful here.
      return Res;
    case clang::Builtin::BI__builtin_signbit: {
      MIRFunction *Callee =
          Builder->GetOrCreateFunction("__signbit", Ty->GetTypeIndex());
      Call = Builder->CreateStmtCall(Callee->GetPuidx(), Args);
      Callee->GetFuncSymbol()->SetAppearsInCode(true);
    } break;
    case clang::Builtin::BI__builtin_sin:
    case clang::Builtin::BIsin:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_sin, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_sinf:
    case clang::Builtin::BIsinf:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_sinf, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_sinh:
    case clang::Builtin::BIsinh:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_sinh, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_sinhf:
    case clang::Builtin::BIsinhf:
      Call = Builder->CreateExprIntrinsicop(INTRN_C_sinhf, OP_intrinsicop, *Ty,
                                            Args);
      break;
    case clang::Builtin::BI__builtin_strcmp:
    case clang::Builtin::BIstrcmp:
      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_strcmp, Args);
      break;
    case clang::Builtin::BI__builtin_strncmp:
    case clang::Builtin::BIstrncmp:
      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_strncmp, Args);
      break;
    case clang::Builtin::BI__builtin_strcpy:
    case clang::Builtin::BIstrcpy:
      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_strcpy, Args);
      break;
    case clang::Builtin::BI__builtin_strncpy:
    case clang::Builtin::BIstrncpy:
      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_strncpy, Args);
      break;
    case clang::Builtin::BI__builtin_strlen:
    case clang::Builtin::BIstrlen:
      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_strlen, Args);
      break;
    case clang::Builtin::BI__builtin_va_copy: {
      ASSERT(Args.size() == 2, "ap_copy expects 2 arguments");
      // The address of the ap_list parameters needs to be passed
      Args[0] = getAddrOfNode(Args[0]);
      Args[1] = getAddrOfNode(Args[1]);

      // Add the size of the ap_list structure as the size to memcpy.
      clang::TypedefDecl *VaListTypeDef = Context->getBuiltinVaListDecl();
      MIRType *APListTy = type2Mpl(VaListTypeDef->getUnderlyingType());
      Args.push_back(Builder->GetConstInt(APListTy->GetSize()));

      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_memcpy, Args);
    } break;
    case clang::Builtin::BI__builtin_va_end:
      // Nothing needs to be done for this function
      return Res;
    case clang::Builtin::BI__builtin_va_start:
      // The address of the ap_list parameter needs to be passed
      Args[0] = getAddrOfNode(Args[0]);

      Call = Builder->CreateStmtIntrinsicCall(INTRN_C_va_start, Args);
      break;
    default: {
#if DEBUG
      if (BuiltinID != 0) {
        LogInfo::MapleLogger()
            << "Unhandled builtin: " << Context->BuiltinInfo.getName(BuiltinID)
            << "\n";
      }
#endif

      llvm::StringRef CalleeName = CalleeDecl->getName();
      if (CalleeName.startswith("__builtin_mpl_vector_")) {
        StringRef VectorOpName =
            CalleeName.substr(strlen("__builtin_mpl_vector_"));

// clang-format off
#define VECTOR_INTRINSIC_TYPE(OP_NAME, VECTY)                                  \
  if (VectorOpName.endswith(#VECTY)) {                                         \
    Call = Builder->CreateExprIntrinsicop(INTRN_vector_##OP_NAME##_##VECTY,    \
                                          OP_intrinsicop, *Ty, Args);          \
  }
#define VECTOR_INTRINSICCALL_TYPE(OP_NAME, VECTY)                              \
  if (VectorOpName.endswith(#VECTY)) {                                         \
    Call = Builder->CreateStmtIntrinsicCall(INTRN_vector_##OP_NAME##_##VECTY,  \
                                            Args);                             \
  }
#define VECTOR_INTRINSIC(OP_NAME)                                              \
  if (VectorOpName.startswith(#OP_NAME)) {                                     \
    VECTOR_INTRINSIC_TYPE(OP_NAME, v2i64)                                      \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v4i32)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v8i16)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v16i8)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v2u64)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v4u32)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v8u16)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v16u8)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v2f64)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v4f32)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v1i64)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v2i32)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v4i16)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v8i8)                                  \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v1u64)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v2u32)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v4u16)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v8u8)                                  \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v1f64)                                 \
    else VECTOR_INTRINSIC_TYPE(OP_NAME, v2f32)                                 \
  }

        VECTOR_INTRINSIC(from_scalar)
        else VECTOR_INTRINSIC(merge)
        else VECTOR_INTRINSIC(reverse)
        else VECTOR_INTRINSIC(set_element)
        else VECTOR_INTRINSIC(sum)
        else VECTOR_INTRINSIC(table_lookup)
        else if (VectorOpName.startswith("get_lane")) {
          VECTOR_INTRINSIC_TYPE(get_lane, v1i64)
          else VECTOR_INTRINSIC_TYPE(get_lane, v2i32)
          else VECTOR_INTRINSIC_TYPE(get_lane, v4i16)
          else VECTOR_INTRINSIC_TYPE(get_lane, v8i8)
          else VECTOR_INTRINSIC_TYPE(get_lane, v1u64)
          else VECTOR_INTRINSIC_TYPE(get_lane, v2u32)
          else VECTOR_INTRINSIC_TYPE(get_lane, v4u16)
          else VECTOR_INTRINSIC_TYPE(get_lane, v8u8)
        } else if (VectorOpName.startswith("getq_lane")) {
          VECTOR_INTRINSIC_TYPE(getq_lane, v2i64)
          else VECTOR_INTRINSIC_TYPE(getq_lane, v4i32)
          else VECTOR_INTRINSIC_TYPE(getq_lane, v8i16)
          else VECTOR_INTRINSIC_TYPE(getq_lane, v16i8)
          else VECTOR_INTRINSIC_TYPE(getq_lane, v2u64)
          else VECTOR_INTRINSIC_TYPE(getq_lane, v4u32)
          else VECTOR_INTRINSIC_TYPE(getq_lane, v8u16)
          else VECTOR_INTRINSIC_TYPE(getq_lane, v16u8)
        } else if (VectorOpName.startswith("abs")) {
          VECTOR_INTRINSIC_TYPE(abs, v8i8)
          else VECTOR_INTRINSIC_TYPE(abs, v4i16)
          else VECTOR_INTRINSIC_TYPE(abs, v2i32)
          else VECTOR_INTRINSIC_TYPE(abs, v1i64)
          else VECTOR_INTRINSIC_TYPE(abs, v2f32)
          else VECTOR_INTRINSIC_TYPE(abs, v1f64)
          else VECTOR_INTRINSIC_TYPE(abs, v16i8)
          else VECTOR_INTRINSIC_TYPE(abs, v8i16)
          else VECTOR_INTRINSIC_TYPE(abs, v4i32)
          else VECTOR_INTRINSIC_TYPE(abs, v2i64)
          else VECTOR_INTRINSIC_TYPE(abs, v4f32)
          else VECTOR_INTRINSIC_TYPE(abs, v2f64)
        } else if (VectorOpName.startswith("addl_low")) {
          VECTOR_INTRINSIC_TYPE(addl_low, v8i8)
          else VECTOR_INTRINSIC_TYPE(addl_low, v4i16)
          else VECTOR_INTRINSIC_TYPE(addl_low, v2i32)
          else VECTOR_INTRINSIC_TYPE(addl_low, v8u8)
          else VECTOR_INTRINSIC_TYPE(addl_low, v4u16)
          else VECTOR_INTRINSIC_TYPE(addl_low, v2u32)
        } else if (VectorOpName.startswith("addl_high")) {
          VECTOR_INTRINSIC_TYPE(addl_high, v8i8)
          else VECTOR_INTRINSIC_TYPE(addl_high, v4i16)
          else VECTOR_INTRINSIC_TYPE(addl_high, v2i32)
          else VECTOR_INTRINSIC_TYPE(addl_high, v8u8)
          else VECTOR_INTRINSIC_TYPE(addl_high, v4u16)
          else VECTOR_INTRINSIC_TYPE(addl_high, v2u32)
        } else if (VectorOpName.startswith("addw_low")) {
          VECTOR_INTRINSIC_TYPE(addw_low, v8i8)
          else VECTOR_INTRINSIC_TYPE(addw_low, v4i16)
          else VECTOR_INTRINSIC_TYPE(addw_low, v2i32)
          else VECTOR_INTRINSIC_TYPE(addw_low, v8u8)
          else VECTOR_INTRINSIC_TYPE(addw_low, v4u16)
          else VECTOR_INTRINSIC_TYPE(addw_low, v2u32)
        } else if (VectorOpName.startswith("addw_high")) {
          VECTOR_INTRINSIC_TYPE(addw_high, v8i8)
          else VECTOR_INTRINSIC_TYPE(addw_high, v4i16)
          else VECTOR_INTRINSIC_TYPE(addw_high, v2i32)
          else VECTOR_INTRINSIC_TYPE(addw_high, v8u8)
          else VECTOR_INTRINSIC_TYPE(addw_high, v4u16)
          else VECTOR_INTRINSIC_TYPE(addw_high, v2u32)
        } else if (VectorOpName.startswith("get_low")) {
          VECTOR_INTRINSIC_TYPE(get_low, v2i64)
          else VECTOR_INTRINSIC_TYPE(get_low, v4i32)
          else VECTOR_INTRINSIC_TYPE(get_low, v8i16)
          else VECTOR_INTRINSIC_TYPE(get_low, v16i8)
          else VECTOR_INTRINSIC_TYPE(get_low, v2u64)
          else VECTOR_INTRINSIC_TYPE(get_low, v4u32)
          else VECTOR_INTRINSIC_TYPE(get_low, v8u16)
          else VECTOR_INTRINSIC_TYPE(get_low, v16u8)
          else VECTOR_INTRINSIC_TYPE(get_low, v2f64)
          else VECTOR_INTRINSIC_TYPE(get_low, v4f32)
        } else if (VectorOpName.startswith("get_high")) {
          VECTOR_INTRINSIC_TYPE(get_high, v2i64)
          else VECTOR_INTRINSIC_TYPE(get_high, v4i32)
          else VECTOR_INTRINSIC_TYPE(get_high, v8i16)
          else VECTOR_INTRINSIC_TYPE(get_high, v16i8)
          else VECTOR_INTRINSIC_TYPE(get_high, v2u64)
          else VECTOR_INTRINSIC_TYPE(get_high, v4u32)
          else VECTOR_INTRINSIC_TYPE(get_high, v8u16)
          else VECTOR_INTRINSIC_TYPE(get_high, v16u8)
          else VECTOR_INTRINSIC_TYPE(get_high, v2f64)
          else VECTOR_INTRINSIC_TYPE(get_high, v4f32)
        } else if (VectorOpName.startswith("narrow_low")) {
          VECTOR_INTRINSIC_TYPE(narrow_low, v2i64)
          else VECTOR_INTRINSIC_TYPE(narrow_low, v4i32)
          else VECTOR_INTRINSIC_TYPE(narrow_low, v8i16)
          else VECTOR_INTRINSIC_TYPE(narrow_low, v2u64)
          else VECTOR_INTRINSIC_TYPE(narrow_low, v4u32)
          else VECTOR_INTRINSIC_TYPE(narrow_low, v8u16)
        } else if (VectorOpName.startswith("narrow_high")) {
          VECTOR_INTRINSIC_TYPE(narrow_high, v2i64)
          else VECTOR_INTRINSIC_TYPE(narrow_high, v4i32)
          else VECTOR_INTRINSIC_TYPE(narrow_high, v8i16)
          else VECTOR_INTRINSIC_TYPE(narrow_high, v2u64)
          else VECTOR_INTRINSIC_TYPE(narrow_high, v4u32)
          else VECTOR_INTRINSIC_TYPE(narrow_high, v8u16)
        } else if (VectorOpName.startswith("shli")) {
          Call = Builder->CreateExprBinary(OP_shl, *Ty, Args[0], Args[1]);
        } else if (VectorOpName.startswith("shri")) {
          Call = Builder->CreateExprBinary(OP_lshr, *Ty, Args[0], Args[1]);
        } else if (VectorOpName.startswith("labssub_low")) {
          VECTOR_INTRINSIC_TYPE(labssub_low, v8i8)
          else VECTOR_INTRINSIC_TYPE(labssub_low, v4i16)
          else VECTOR_INTRINSIC_TYPE(labssub_low, v2i32)
          else VECTOR_INTRINSIC_TYPE(labssub_low, v8u8)
          else VECTOR_INTRINSIC_TYPE(labssub_low, v4u16)
          else VECTOR_INTRINSIC_TYPE(labssub_low, v2u32)
        } else if (VectorOpName.startswith("labssub_high")) {
          VECTOR_INTRINSIC_TYPE(labssub_high, v8i8)
          else VECTOR_INTRINSIC_TYPE(labssub_high, v4i16)
          else VECTOR_INTRINSIC_TYPE(labssub_high, v2i32)
          else VECTOR_INTRINSIC_TYPE(labssub_high, v8u8)
          else VECTOR_INTRINSIC_TYPE(labssub_high, v4u16)
          else VECTOR_INTRINSIC_TYPE(labssub_high, v2u32)
        } else if (VectorOpName.startswith("shr_narrow_low")) {
          VECTOR_INTRINSIC_TYPE(shr_narrow_low, v2i64)
          else VECTOR_INTRINSIC_TYPE(shr_narrow_low, v4i32)
          else VECTOR_INTRINSIC_TYPE(shr_narrow_low, v8i16)
          else VECTOR_INTRINSIC_TYPE(shr_narrow_low, v2u64)
          else VECTOR_INTRINSIC_TYPE(shr_narrow_low, v4u32)
          else VECTOR_INTRINSIC_TYPE(shr_narrow_low, v8u16)
        } else if (VectorOpName.startswith("pairwise_adalp")) {
          VECTOR_INTRINSIC_TYPE(pairwise_adalp, v8i8)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v4i16)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v2i32)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v8u8)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v4u16)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v2u32)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v16i8)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v8i16)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v4i32)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v16u8)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v8u16)
          else VECTOR_INTRINSIC_TYPE(pairwise_adalp, v4u32)
        } else if (VectorOpName.startswith("pairwise_add")) {
          VECTOR_INTRINSIC_TYPE(pairwise_add, v4i32)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v8i16)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v16i8)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v4u32)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v8u16)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v16u8)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v2i32)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v4i16)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v8i8)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v2u32)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v4u16)
          else VECTOR_INTRINSIC_TYPE(pairwise_add, v8u8)
        } else if (VectorOpName.startswith("madd")) {
          VECTOR_INTRINSIC_TYPE(madd, v2i32)
          else VECTOR_INTRINSIC_TYPE(madd, v4i16)
          else VECTOR_INTRINSIC_TYPE(madd, v8i8)
          else VECTOR_INTRINSIC_TYPE(madd, v2u32)
          else VECTOR_INTRINSIC_TYPE(madd, v4u16)
          else VECTOR_INTRINSIC_TYPE(madd, v8u8)
        } else if (VectorOpName.startswith("mull_low")) {
          VECTOR_INTRINSIC_TYPE(mull_low, v2i32)
          else VECTOR_INTRINSIC_TYPE(mull_low, v4i16)
          else VECTOR_INTRINSIC_TYPE(mull_low, v8i8)
          else VECTOR_INTRINSIC_TYPE(mull_low, v2u32)
          else VECTOR_INTRINSIC_TYPE(mull_low, v4u16)
          else VECTOR_INTRINSIC_TYPE(mull_low, v8u8)
        } else if (VectorOpName.startswith("mull_high")) {
          VECTOR_INTRINSIC_TYPE(mull_high, v2i32)
          else VECTOR_INTRINSIC_TYPE(mull_high, v4i16)
          else VECTOR_INTRINSIC_TYPE(mull_high, v8i8)
          else VECTOR_INTRINSIC_TYPE(mull_high, v2u32)
          else VECTOR_INTRINSIC_TYPE(mull_high, v4u16)
          else VECTOR_INTRINSIC_TYPE(mull_high, v8u8)
        } else if (VectorOpName.startswith("subl_low")) {
          VECTOR_INTRINSIC_TYPE(subl_low, v8i8)
          else VECTOR_INTRINSIC_TYPE(subl_low, v4i16)
          else VECTOR_INTRINSIC_TYPE(subl_low, v2i32)
          else VECTOR_INTRINSIC_TYPE(subl_low, v8u8)
          else VECTOR_INTRINSIC_TYPE(subl_low, v4u16)
          else VECTOR_INTRINSIC_TYPE(subl_low, v2u32)
        } else if (VectorOpName.startswith("subl_high")) {
          VECTOR_INTRINSIC_TYPE(subl_high, v8i8)
          else VECTOR_INTRINSIC_TYPE(subl_high, v4i16)
          else VECTOR_INTRINSIC_TYPE(subl_high, v2i32)
          else VECTOR_INTRINSIC_TYPE(subl_high, v8u8)
          else VECTOR_INTRINSIC_TYPE(subl_high, v4u16)
          else VECTOR_INTRINSIC_TYPE(subl_high, v2u32)
        } else if (VectorOpName.startswith("subw_low")) {
          VECTOR_INTRINSIC_TYPE(subw_low, v8i8)
          else VECTOR_INTRINSIC_TYPE(subw_low, v4i16)
          else VECTOR_INTRINSIC_TYPE(subw_low, v2i32)
          else VECTOR_INTRINSIC_TYPE(subw_low, v8u8)
          else VECTOR_INTRINSIC_TYPE(subw_low, v4u16)
          else VECTOR_INTRINSIC_TYPE(subw_low, v2u32)
        } else if (VectorOpName.startswith("subw_high")) {
          VECTOR_INTRINSIC_TYPE(subw_high, v8i8)
          else VECTOR_INTRINSIC_TYPE(subw_high, v4i16)
          else VECTOR_INTRINSIC_TYPE(subw_high, v2i32)
          else VECTOR_INTRINSIC_TYPE(subw_high, v8u8)
          else VECTOR_INTRINSIC_TYPE(subw_high, v4u16)
          else VECTOR_INTRINSIC_TYPE(subw_high, v2u32)
        } else if (VectorOpName.startswith("widen_low")) {
          VECTOR_INTRINSIC_TYPE(widen_low, v2i32)
          else VECTOR_INTRINSIC_TYPE(widen_low, v4i16)
          else VECTOR_INTRINSIC_TYPE(widen_low, v8i8)
          else VECTOR_INTRINSIC_TYPE(widen_low, v2u32)
          else VECTOR_INTRINSIC_TYPE(widen_low, v4u16)
          else VECTOR_INTRINSIC_TYPE(widen_low, v8u8)
        } else if (VectorOpName.startswith("widen_high")) {
          VECTOR_INTRINSIC_TYPE(widen_high, v2i32)
          else VECTOR_INTRINSIC_TYPE(widen_high, v4i16)
          else VECTOR_INTRINSIC_TYPE(widen_high, v8i8)
          else VECTOR_INTRINSIC_TYPE(widen_high, v2u32)
          else VECTOR_INTRINSIC_TYPE(widen_high, v4u16)
          else VECTOR_INTRINSIC_TYPE(widen_high, v8u8)
        } else if (VectorOpName.startswith("zip1q")) {
          VECTOR_INTRINSIC_TYPE(zip1q, v16i8)
          else VECTOR_INTRINSIC_TYPE(zip1q, v8i16)
          else VECTOR_INTRINSIC_TYPE(zip1q, v4i32)
          else VECTOR_INTRINSIC_TYPE(zip1q, v2i64)
          else VECTOR_INTRINSIC_TYPE(zip1q, v16u8)
          else VECTOR_INTRINSIC_TYPE(zip1q, v8u16)
          else VECTOR_INTRINSIC_TYPE(zip1q, v4u32)
          else VECTOR_INTRINSIC_TYPE(zip1q, v2u64)
        } else if (VectorOpName.startswith("zip2q")) {
          VECTOR_INTRINSIC_TYPE(zip2q, v16i8)
          else VECTOR_INTRINSIC_TYPE(zip2q, v8i16)
          else VECTOR_INTRINSIC_TYPE(zip2q, v4i32)
          else VECTOR_INTRINSIC_TYPE(zip2q, v2i64)
          else VECTOR_INTRINSIC_TYPE(zip2q, v16u8)
          else VECTOR_INTRINSIC_TYPE(zip2q, v8u16)
          else VECTOR_INTRINSIC_TYPE(zip2q, v4u32)
          else VECTOR_INTRINSIC_TYPE(zip2q, v2u64)
        } else if (VectorOpName.startswith("zip1")) {
          VECTOR_INTRINSIC_TYPE(zip1, v8i8)
          else VECTOR_INTRINSIC_TYPE(zip1, v4i16)
          else VECTOR_INTRINSIC_TYPE(zip1, v2i32)
          else VECTOR_INTRINSIC_TYPE(zip1, v8u8)
          else VECTOR_INTRINSIC_TYPE(zip1, v4u16)
          else VECTOR_INTRINSIC_TYPE(zip1, v2u32)
          else VECTOR_INTRINSIC_TYPE(zip1q, v8u16)
        } else if (VectorOpName.startswith("zip2")) {
          VECTOR_INTRINSIC_TYPE(zip2, v8i8)
          else VECTOR_INTRINSIC_TYPE(zip2, v4i16)
          else VECTOR_INTRINSIC_TYPE(zip2, v2i32)
          else VECTOR_INTRINSIC_TYPE(zip2, v8u8)
          else VECTOR_INTRINSIC_TYPE(zip2, v4u16)
          else VECTOR_INTRINSIC_TYPE(zip2, v2u32)
        }
        // clang-format on
        else if (VectorOpName.startswith("load")) {
          Call = Builder->CreateExprIread(
              *Ty,
              *GlobalTables::GetTypeTable().GetOrCreatePointerType(
                  *Ty, PointerPrimTy),
              0, Args[0]);
        }
        else if (VectorOpName.startswith("store")) {
          Call = Builder->CreateStmtIassign(
              *GlobalTables::GetTypeTable().GetOrCreatePointerType(
                  type2MplIdx(CE->getArg(1)->getType()), PointerPrimTy),
              0, Args[0], Args[1]);
        }
      } else if (CalleeDecl->getName().startswith("__builtin_")) {
        MIRFunction *Callee = Builder->GetOrCreateFunction(
            CalleeDecl->getName().substr(10).str(), Ty->GetTypeIndex());
        Call = Builder->CreateStmtCall(Callee->GetPuidx(), Args);
        Callee->GetFuncSymbol()->SetAppearsInCode(true);
      } else if (CalleeDecl->getName() == "alloca") {
        Call = Builder->CreateExprUnary(OP_alloca, *Ty, Args[0]);
        Call->SetPrimType(PointerPrimTy);
      } else {
        MIRSymbol *CalleeSym = decl2Mpl(CalleeDecl);
        MIRFunction *Callee = CalleeSym->GetFunction();

        Call = Builder->CreateStmtCall(Callee->GetPuidx(), Args);
        Callee->GetFuncSymbol()->SetAppearsInCode(true);
      }
    }
    }

    Res.setNode(Call, CE->getID(*Context), Ty, Attrs);
    return Res;
  }
}

Result Clang2MapleVisitor::VisitCastExpr(const clang::CastExpr *CE) {
  Result SubExprRes = Visit(CE->getSubExpr());
  MIRType *FromTy = type2Mpl(CE->getSubExpr()->getType());
  MIRType *ToTy = type2Mpl(CE->getType());

  Result Res(CE->getExprLoc());
  switch (CE->getCastKind()) {
  case clang::CK_BitCast: {
    if (FromTy->GetPrimType() == ToTy->GetPrimType() &&
        FromTy->IsScalarType()) {
      // This case may show up when casting from a 1-element vector to its
      // scalar type.
      return SubExprRes;
    }

    BaseNode *SubExpr = getNodeAsRVal(SubExprRes);
    if (ToTy->GetKind() == kTypePointer) {
      Res.setNode(SubExpr, CE->getID(*Context), ToTy);
    } else {
      BaseNode *SubExpr = getNodeAsRVal(SubExprRes);
      Res.setNode(Builder->CreateExprRetype(*ToTy, *FromTy, SubExpr),
                  CE->getID(*Context), ToTy);
    }
  } break;
  // This must be handled by the parent node
  case clang::CK_LValueToRValue:
  // Nothing needs to be passed down into the IR for these
  case clang::CK_NoOp:
  case clang::CK_ToVoid:
  case clang::CK_ToUnion:
    return SubExprRes;
  case clang::CK_ArrayToPointerDecay: {
    // A variable array type has been converted to a pointer, so do not take its
    // address here.
    if (CE->getSubExpr()->getType()->isVariableArrayType()) {
      return SubExprRes;
    }

    if (SubExprRes.isSym()) {
      BaseNode *AddrOf = Builder->CreateExprAddrof(SubExprRes.getField(),
                                                   *SubExprRes.getSym());
      AddrOf->SetPrimType(PointerPrimTy);
      Result NodeRes(AddrOf, CE->getID(*Context), ToTy, FromTy,
                     CE->getExprLoc());
      NodeRes.appendStmts(SubExprRes);
      return NodeRes;
    }

    if (SubExprRes.getField()) {
      Result FieldRes(Builder->CreateExprIaddrof(*ToTy, *SubExprRes.getAddrTy(),
                                                 SubExprRes.getField(),
                                                 SubExprRes.getNode()),
                      CE->getID(*Context), ToTy, FromTy, CE->getExprLoc());
      FieldRes.appendStmts(SubExprRes);
      return FieldRes;
    }

    Result Res(getNodeAsRVal(SubExprRes), CE->getID(*Context), ToTy, FromTy,
               CE->getExprLoc());
    Res.appendStmts(SubExprRes);
    return Res;
  }
  case clang::CK_NullToPointer:
  case clang::CK_IntegralToPointer: {
    // Change the type of the const
    if (BaseNode *N = SubExprRes.getNode()) {
      if (N->GetOpCode() == OP_constval) {
        ConstvalNode *ConstNode = static_cast<ConstvalNode *>(N);
        MIRConst *Const = ConstNode->GetConstVal();
        ASSERT(Const->GetKind() == kConstInt, "Expected constant int in cast");
        MIRIntConst *IntConst = static_cast<MIRIntConst *>(Const);
        ConstNode->SetConstVal(
            GlobalTables::GetIntConstTable().GetOrCreateIntConst(
                IsSignedInteger(N->GetPrimType()) ? IntConst->GetValue().GetSXTValue() : IntConst->GetValue().GetZXTValue(), *IntPointerTy));
      }
      N->SetPrimType(PointerPrimTy);
    }
    SubExprRes.setValueTy(ToTy, TypeAttrs(), true);
    return SubExprRes;
  }
  case clang::CK_FunctionToPointerDecay:
    // This is just a type change
    SubExprRes.setValueTy(ToTy);
    return SubExprRes;
  case clang::CK_IntegralCast: {
    clang::Expr::EvalResult Constant;
    if (CE->EvaluateAsConstantExpr(Constant, *Context)) {
      // Don't generate casts for constants, just emit the constant
      if (!Constant.Val.isInt()) {
        LogInfo::MapleLogger()
            << "Error: expected integer constant in integral cast\n";
        Constant.Val.dump();
      }
      llvm::APSInt Value = Constant.Val.getInt();
      PrimType PTy = ToTy->GetPrimType();
      if (!Initializer) {
        PTy = GetRegPrimType(PTy);
      }
      BaseNode *ConstNode = Builder->CreateIntConst(Value.getExtValue(), PTy);
      Res.setNode(ConstNode, CE->getID(*Context), ToTy);
    } else {
      Res.setNode(createNumericCast(ToTy, getNodeAsRVal(SubExprRes)),
                  CE->getID(*Context), ToTy);
    }
  } break;
  case clang::CK_IntegralToBoolean:
  case clang::CK_PointerToBoolean:
  case clang::CK_FloatingToBoolean:
    Res.setNode(exprToCond(getNodeAsRVal(SubExprRes)), CE->getID(*Context),
                ToTy);
    break;
  case clang::CK_PointerToIntegral:
    Res.setNode(createNumericCast(ToTy, getNodeAsRVal(SubExprRes)),
                CE->getID(*Context), ToTy);
    break;
  case clang::CK_IntegralToFloating:
  case clang::CK_FloatingCast:
    // Because we treat `long double` as `double`, this may be a cast to/from
    // the same type. If so, do not generate a convert node.
    if (ToTy == FromTy) {
      return SubExprRes;
    }
    Res.setNode(Builder->CreateExprTypeCvt(OP_cvt, *ToTy, *FromTy,
                                           getNodeAsRVal(SubExprRes)),
                CE->getID(*Context), ToTy);
    break;
  case clang::CK_FloatingToIntegral: {
    BaseNode *SubExpr = getNodeAsRVal(SubExprRes);
    if (ToTy->GetSize() < 4) {
      bool IsUnsigned = PrimitiveType(ToTy->GetPrimType()).IsUnsigned();
      TypeCvtNode *Cvt =
          Builder->CreateExprTypeCvt(OP_cvt, IsUnsigned ? PTY_u32 : PTY_i32,
                                     FromTy->GetPrimType(), *SubExpr);
      Res.setNode(Builder->CreateExprExtractbits(
                      IsUnsigned ? OP_zext : OP_sext,
                      IsUnsigned ? *GlobalTables::GetTypeTable().GetUInt32()
                                 : *GlobalTables::GetTypeTable().GetInt32(),
                      0, ToTy->GetSize() * 8, Cvt),
                  CE->getID(*Context), ToTy);
    } else {
      Res.setNode(Builder->CreateExprTypeCvt(OP_cvt, *ToTy, *FromTy, SubExpr),
                  CE->getID(*Context), ToTy);
    }
  } break;
  case clang::CK_VectorSplat: {
    BaseNode *SubExpr = getNodeAsRVal(SubExprRes);
    PrimType PTy = ToTy->GetPrimType();
    MapleVector<BaseNode *> Args(
        Builder->GetCurrentFuncCodeMpAllocator()->Adapter());
    Args.push_back(createNumericCast(
        GlobalTables::GetTypeTable().GetPrimType(getVectorElementPrimType(PTy)),
        SubExpr));

#define CASE_VECTOR_FROM_SCALAR(VECTY)                                         \
  case PTY_##VECTY:                                                            \
    Res.setNode(                                                               \
        Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_##VECTY,       \
                                       OP_intrinsicop, *ToTy, Args),           \
        CE->getID(*Context), ToTy);                                            \
    break;

    switch (PTy) {
      CASE_VECTOR_FROM_SCALAR(v2i64)
      CASE_VECTOR_FROM_SCALAR(v4i32)
      CASE_VECTOR_FROM_SCALAR(v8i16)
      CASE_VECTOR_FROM_SCALAR(v16i8)
      CASE_VECTOR_FROM_SCALAR(v2u64)
      CASE_VECTOR_FROM_SCALAR(v4u32)
      CASE_VECTOR_FROM_SCALAR(v8u16)
      CASE_VECTOR_FROM_SCALAR(v16u8)
      CASE_VECTOR_FROM_SCALAR(v2f64)
      CASE_VECTOR_FROM_SCALAR(v4f32)
      CASE_VECTOR_FROM_SCALAR(v2i32)
      CASE_VECTOR_FROM_SCALAR(v4i16)
      CASE_VECTOR_FROM_SCALAR(v8i8)
      CASE_VECTOR_FROM_SCALAR(v2u32)
      CASE_VECTOR_FROM_SCALAR(v4u16)
      CASE_VECTOR_FROM_SCALAR(v8u8)
      CASE_VECTOR_FROM_SCALAR(v2f32)
    case PTY_i64: {
      ASSERT(isOneElementVector(CE->getType()),
             "unexpected scalar type in VectorSplat cast");
      TypeAttrs Attrs;
      Attrs.SetAttr(ATTR_oneelem_simd);
      Res.setNode(Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v1i64,
                                                 OP_intrinsicop, *ToTy, Args),
                  CE->getID(*Context), ToTy, Attrs);
    } break;
    case PTY_u64: {
      ASSERT(isOneElementVector(CE->getType()),
             "unexpected scalar type in VectorSplat cast");
      TypeAttrs Attrs;
      Attrs.SetAttr(ATTR_oneelem_simd);
      Res.setNode(Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v1u64,
                                                 OP_intrinsicop, *ToTy, Args),
                  CE->getID(*Context), ToTy, Attrs);
    } break;
    case PTY_f64: {
      ASSERT(isOneElementVector(CE->getType()),
             "unexpected scalar type in VectorSplat cast");
      TypeAttrs Attrs;
      Attrs.SetAttr(ATTR_oneelem_simd);
      Res.setNode(Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v1f64,
                                                 OP_intrinsicop, *ToTy, Args),
                  CE->getID(*Context), ToTy, Attrs);
    } break;
    default:
      LogInfo::MapleLogger()
          << "Error: Unhandled vector type in VisitCastExpr\n";
#ifdef DEBUG
      ToTy->Dump(0);
#endif
      CHECK_FATAL(false, "Failing due to unsupported feature");
    }
  } break;

  default:
    LogInfo::MapleLogger() << "Error: Unhandled cast kind: "
                           << CE->getCastKindName() << "\n";
    CHECK_FATAL(false, "Failing due to unsupported feature");
  }

  Res.appendStmts(SubExprRes);
  return Res;
}

Result
Clang2MapleVisitor::VisitCharacterLiteral(const clang::CharacterLiteral *CL) {
  MIRType *Ty = type2Mpl(CL->getType());
  ConstvalNode *Const =
      Builder->CreateIntConst(CL->getValue(), Ty->GetPrimType());

  return Result(Const, CL->getID(*Context), CL->getLocation(), Ty);
}

Result Clang2MapleVisitor::VisitChooseExpr(const clang::ChooseExpr *CE) {
  return Visit(CE->getChosenSubExpr());
}

Result Clang2MapleVisitor::VisitCompoundLiteralExpr(
    const clang::CompoundLiteralExpr *CLE) {
  MIRType *Ty = type2Mpl(CLE->getType());
  ASSERT(Ty->IsStructType() || Ty->GetKind() == kTypeArray ||
             PrimitiveType(Ty->GetPrimType()).IsVector(),
         "Unexpected type of CompoundLiteralExpr (not struct or array)");

  // Create a temporary value for this structure and initialize it using this
  // literal expression
  if (Builder->GetCurrentFunction()) {
    Result InitRes = Visit(CLE->getInitializer());
    MIRSymbol *Temp = Builder->GetOrCreateLocalDecl(
        "cle." + std::to_string(++UnnamedCount), *Ty);
    Result Res(Temp, Ty, CLE->getBeginLoc());
    Res.appendStmts(InitRes);
    BaseNode *InitNode = InitRes.getNode();
    if (InitNode) {
      if (Ty->IsStructType()) {
        MIRStructType *StructTy = static_cast<MIRStructType *>(Ty);
        assignStruct(Res, Temp, nullptr, StructTy, InitNode);
      } else if (PrimitiveType(Ty->GetPrimType()).IsVector()) {
        assignVector(Res, Temp, Ty, InitNode);
      } else { // array
        MIRArrayType *ArrayTy = static_cast<MIRArrayType *>(Ty);
        BaseNode *Base = Builder->CreateExprAddrof(0, *Temp);
        Base->SetPrimType(PointerPrimTy);
        assignArray(Res, Base, ArrayTy, InitNode);
      }
    }

    return Res;
  } else {
    Initializer = true;
    MIRConst *Init = getInitializer(Ty, CLE->getInitializer());
    Initializer = false;
    MIRSymbol *Temp =
        Builder->CreateGlobalDecl("cle." + std::to_string(++UnnamedCount), *Ty);
    Temp->SetKonst(Init);
    return Result(Temp, Ty, CLE->getBeginLoc());
  }
}

Result Clang2MapleVisitor::VisitConditionalOperator(
    const clang::ConditionalOperator *CO) {
  // First, check if the condition is a compile-time constant
  clang::Expr::EvalResult R;
  bool Success = CO->getCond()->EvaluateAsInt(R, *Context);
  if (Success) {
    llvm::APSInt Val = R.Val.getInt();
    if (Val.isNullValue()) {
      return Visit(CO->getFalseExpr());
    } else {
      return Visit(CO->getTrueExpr());
    }
  }

  Result CondRes = Visit(CO->getCond());
  Result TrueRes = Visit(CO->getTrueExpr());
  Result FalseRes = Visit(CO->getFalseExpr());
  MIRType *Ty = type2Mpl(CO->getType());

  // In the simple case, both true and false are just one expression
  if (Ty->GetPrimType() != PTY_void && TrueRes.isSimple() &&
      TrueRes.canSpeculate() && FalseRes.isSimple() &&
      FalseRes.canSpeculate()) {
    Result Res(Builder->CreateExprTernary(
                   OP_select, *Ty, exprToCond(getNodeAsRVal(CondRes)),
                   getNodeAsRVal(TrueRes), getNodeAsRVal(FalseRes)),
               CO->getID(*Context), CO->getBeginLoc(), Ty);
    Res.appendStmts(CondRes);
    return Res;
  }

  // If not, then expand to an if-then-else
  MIRSymbol *Temp = nullptr;
  if (Ty->GetPrimType() != PTY_void) {
    Temp = Builder->GetOrCreateLocalDecl(
        "cond." + std::to_string(CO->getID(*Context)), *Ty);
  }
  Result Res(Temp, Ty, CO->getBeginLoc());

  IfStmtNode *IfNode =
      Builder->CreateStmtIf(exprToCond(getNodeAsRVal(CondRes)));
  Res.appendStmts(CondRes);

  BlockNode *Block = Builder->GetCurrentFuncCodeMp()->New<BlockNode>();
  StmtNode *TruePart;
  if (Temp) {
    TruePart = Builder->CreateStmtDassign(*Temp, 0, getNodeAsRVal(TrueRes));
  } else {
    TruePart = getNodeAsStmt(TrueRes);
  }

  for (Result::iterator It = TrueRes.beginStmtsBefore();
       It != TrueRes.endStmtsBefore(); ++It) {
    Block->AddStatement(*It);
  }
  addStmtToBlock(Block, TruePart);
  for (Result::iterator It = TrueRes.beginStmtsAfter();
       It != TrueRes.endStmtsAfter(); ++It) {
    Block->AddStatement(*It);
  }
  IfNode->SetThenPart(Block);

  Block = Builder->GetCurrentFuncCodeMp()->New<BlockNode>();
  StmtNode *FalsePart;
  if (Temp) {
    FalsePart = Builder->CreateStmtDassign(*Temp, 0, getNodeAsRVal(FalseRes));
  } else {
    FalsePart = getNodeAsStmt(FalseRes);
  }

  for (Result::iterator It = FalseRes.beginStmtsBefore();
       It != FalseRes.endStmtsBefore(); ++It) {
    Block->AddStatement(*It);
  }
  addStmtToBlock(Block, FalsePart);
  for (Result::iterator It = FalseRes.beginStmtsAfter();
       It != FalseRes.endStmtsAfter(); ++It) {
    Block->AddStatement(*It);
  }
  IfNode->SetElsePart(Block);

  IfNode->SetSrcPos(sourceLocation2Mpl(CO->getBeginLoc()));
  Res.appendStmtBefore(IfNode);
  return Res;
}

Result Clang2MapleVisitor::VisitConstantExpr(const clang::ConstantExpr *CE) {
  // This is just a wrapper expression
  return Visit(CE->getSubExpr());
}

Result Clang2MapleVisitor::VisitDeclRefExpr(const clang::DeclRefExpr *DRE) {
  MIRType *Ty = type2Mpl(DRE->getType());
  const clang::ValueDecl *D = DRE->getDecl();

  // We do not actually create symbols for enums, just extract the constant
  if (auto EnumConst = llvm::dyn_cast<clang::EnumConstantDecl>(D)) {
    const llvm::APSInt Value = EnumConst->getInitVal();
    ConstvalNode *ConstNode =
        Builder->CreateIntConst(Value.getExtValue(), Ty->GetPrimType());
    return Result(ConstNode, DRE->getID(*Context), DRE->getLocation(), Ty);
  }

  MIRSymbol *Sym = decl2Mpl(D);

  return Result(Sym, Ty, DRE->getLocation());
}

Result
Clang2MapleVisitor::VisitFloatingLiteral(const clang::FloatingLiteral *Lit) {
  MIRType *Ty = type2Mpl(Lit->getType());
  llvm::APFloat Value = Lit->getValue();
  ConstvalNode *Const = nullptr;
  if (&Value.getSemantics() == &llvm::APFloat::IEEEsingle()) {
    Const = Builder->CreateFloatConst(Value.convertToFloat());
  } else if (&Value.getSemantics() == &llvm::APFloat::IEEEdouble()) {
    Const = Builder->CreateDoubleConst(Value.convertToDouble());
  } else if (&Value.getSemantics() == &llvm::APFloat::IEEEquad()) {
    bool LosesInfo;
    Value.convert(llvm::APFloat::IEEEdouble(),
                  llvm::APFloatBase::roundingMode::NearestTiesToAway,
                  &LosesInfo);
    Const = Builder->CreateDoubleConst(Value.convertToDouble());
  } else {
    LogInfo::MapleLogger() << "Unexpected type of floating literal: "
                           << Ty->GetPrimType() << "\n";
  }
  return Result(Const, Lit->getID(*Context), Lit->getLocation(), Ty);
}

Result Clang2MapleVisitor::VisitImplicitValueInitExpr(
    const clang::ImplicitValueInitExpr *IV) {
  MIRType *Ty = type2Mpl(IV->getType());
  MIRConst *Zero = createZero(Ty);
  ConstvalNode *ZeroNode = Builder->CreateConstval(Zero);
  return Result(ZeroNode, IV->getID(*Context), IV->getBeginLoc(), Ty);
}

Result Clang2MapleVisitor::VisitInitListExpr(const clang::InitListExpr *ILE) {
  bool isStruct = ILE->getType()->isStructureType();
  bool isUnion = ILE->getType()->isUnionType();
  clang::RecordDecl *Record = ILE->getType()->getAsRecordDecl();
  MIRType *Ty = type2Mpl(ILE->getType());

  if (ILE->getNumInits() == 0) {
    // Avoid generating an empty aggregate
    if (Ty->GetSize() == 0) {
      return Result(ILE->getBeginLoc());
    }

    MIRConst *Zero = createZero(Ty);
    return Result(Builder->CreateConstval(Zero), ILE->getID(*Context),
                  ILE->getBeginLoc(), Ty);
  }

  // Ignore an init list expression for a scalar. For example:
  //   int x[3] = {{3}};
  if (Ty->IsScalarType() && !PrimitiveType(Ty->GetPrimType()).IsVector()) {
    return Visit(ILE->getInit(0));
  }

  if (ILE->isConstantInitializer(*Context, false)) {
    // Constant initializers return an agg const and may be used to initialize a
    // global.
    MIRAggConst *Agg;
    if (ILE->isStringLiteralInit()) {
      // String literals are a special case. getInitializer will return the
      // complete agg, rather than processing it as an array.
      MIRConst *Const = getInitializer(Ty, ILE->getInit(0));
      ASSERT(Const->GetKind() == kConstAggConst,
             "Expected agg const for string literal");
      Agg = static_cast<MIRAggConst *>(Const);
    } else {
      Agg = Builder->GetCurrentFuncCodeMp()->New<MIRAggConst>(*Module, *Ty);
      if (isStruct) {
        unsigned InitIndex = 0;
        for (const auto *FD : Record->fields()) {
          if (FD->isUnnamedBitfield()) {
            continue;
          }

          MIRType *InitTy = type2Mpl(ILE->getInit(InitIndex)->getType());

          // Skip zero-sized structs
          if (InitTy->GetSize() == 0) {
            InitIndex++;
            continue;
          }

          MIRConst *Const =
              evaluateExprAsConst(ILE->getInit(InitIndex), InitTy);
          if (!Const) {
            Const = getInitializer(InitTy, ILE->getInit(InitIndex));
          }
          ASSERT(Const, "Failed to get constant initializer for field");

          if (FD->isBitField()) {
            ASSERT(Const->GetKind() == kConstInt,
                   "Expected int for bitfield initialization");
            MIRIntConst *IntConst = static_cast<MIRIntConst *>(Const);
            unsigned long Mask = (1L << FD->getBitWidthValue(*Context)) - 1;
            Const = GlobalTables::GetIntConstTable().GetOrCreateIntConst(
                IntConst->GetValue().GetZXTValue() & Mask, *InitTy);
          }

          // If this agg const will be used as a global initializer, use the
          // field index, but if it will be expanded in a function body, use
          // the field ID.
          FieldID Field;
          if (Builder->GetCurrentFunction()) {
            Field = FieldMap[Record][0][FD];
          } else {
            Field = FD->getFieldIndex() + 1;
          }
          Agg->AddItem(Const, Field);

          InitIndex++;
        }
      } else if (isUnion) {
        MIRType *InitTy = type2Mpl(ILE->getInit(0)->getType());
        MIRConst *Const = getInitializer(InitTy, ILE->getInit(0));
        const clang::FieldDecl *FD = ILE->getInitializedFieldInUnion();
        Agg->AddItem(Const, FieldMap[Record][0][FD]);
      } else {
        for (unsigned i = 0; i < ILE->getNumInits(); ++i) {
          MIRType *InitTy = type2Mpl(ILE->getInit(i)->getType());
          MIRConst *Const = getInitializer(InitTy, ILE->getInit(i));
          Agg->PushBack(Const);
        }
      }
    }
    ConstvalNode *ConstNode =
        Builder->GetCurrentFuncCodeMp()->New<ConstvalNode>(PTY_agg, Agg);
    return Result(ConstNode, ILE->getID(*Context), ILE->getBeginLoc(), Ty);
  } else {
    Result Res(ILE->getBeginLoc());

    // This will get translated into a series of assign nodes. For now, just
    // return an intrinsic op node with all of the expressions. For structs and
    // unions, the expressions are wrapped in an iread node to store the field
    // ID.
    MapleVector<BaseNode *> Ops(
        Builder->GetCurrentFuncCodeMpAllocator()->Adapter());
    if (isStruct) {
      unsigned InitIndex = 0;
      for (const auto *FD : Record->fields()) {
        // Skip unnamed bitfields
        if (FD->isUnnamedBitfield()) {
          continue;
        }

        Result InitRes = Visit(ILE->getInit(InitIndex));
        BaseNode *InitNode = getNodeAsRVal(InitRes);
        Res.appendStmts(InitRes);
        Ops.push_back(Builder->CreateExprIread(
            *InitRes.getValueTy(), *Ty, FieldMap[Record][0][FD], InitNode));
        InitIndex++;
      }
    } else if (isUnion) {
      Result InitRes = Visit(ILE->getInit(0));
      BaseNode *InitNode = getNodeAsRVal(InitRes);
      Res.appendStmts(InitRes);
      const clang::FieldDecl *FD = ILE->getInitializedFieldInUnion();
      Ops.push_back(Builder->CreateExprIread(
          *InitRes.getValueTy(), *Ty, FieldMap[Record][0][FD], InitNode));
    } else {
      for (unsigned i = 0; i < ILE->getNumInits(); ++i) {
        Result InitRes = Visit(ILE->getInit(i));
        BaseNode *InitNode = getNodeAsRVal(InitRes);
        Res.appendStmts(InitRes);
        Ops.push_back(InitNode);
      }
    }
    IntrinsicopNode *N = Builder->CreateExprIntrinsicop(
        INTRN_UNDEFINED, OP_intrinsicopwithtype, *Ty, Ops);
    Res.setNode(N, ILE->getID(*Context), Ty);
    return Res;
  }
}

Result
Clang2MapleVisitor::VisitIntegerLiteral(const clang::IntegerLiteral *Lit) {
  MIRType *Ty = type2Mpl(Lit->getType());
  ConstvalNode *Const = Builder->CreateIntConst(Lit->getValue().getSExtValue(),
                                                Ty->GetPrimType());

  return Result(Const, Lit->getID(*Context), Lit->getLocation(), Ty);
}

Result Clang2MapleVisitor::VisitMemberExpr(const clang::MemberExpr *ME) {
  clang::Expr *BaseExpr = ME->getBase();

  clang::QualType QT = BaseExpr->getType();
  if (ME->isArrow()) {
    QT = QT->getPointeeType();
  }
  clang::RecordDecl *Record = QT->getAsRecordDecl();

  Result BaseRes = Visit(BaseExpr);
  const clang::FieldDecl *FD =
      llvm::dyn_cast<clang::FieldDecl>(ME->getMemberDecl());
  ASSERT(FD, "member is not a FieldDecl");

  // Handle member access through a pointer
  if (ME->isArrow()) {
    BaseNode *Base = getNodeAsRVal(BaseRes);
    Result Res(Base, ME->getID(*Context), type2Mpl(BaseExpr->getType()),
               type2Mpl(ME->getType()), ME->getExprLoc());
    Res.appendStmts(BaseRes);
    Res.setBaseRecordDecl(Record);
    ASSERT(FieldMap.find(Record) != FieldMap.end() &&
               FieldMap[Record].find(0) != FieldMap[Record].end() &&
               FieldMap[Record][0].find(FD) != FieldMap[Record][0].end(),
           "Structure or field not in map");
    Res.setField(FieldMap[Record][0][FD]);
    return Res;
  } else {
    Record = BaseRes.setBaseRecordDecl(Record);
    ASSERT(FieldMap.find(Record) != FieldMap.end() &&
               FieldMap[Record].find(BaseRes.getField()) !=
                   FieldMap[Record].end() &&
               FieldMap[Record][BaseRes.getField()].find(FD) !=
                   FieldMap[Record][BaseRes.getField()].end(),
           "Structure or field not in map");
    BaseRes.setField(FieldMap[Record][BaseRes.getField()][FD]);
    BaseRes.setValueTy(type2Mpl(ME->getType()), TypeAttrs(), true);
    return BaseRes;
  }
}

Result Clang2MapleVisitor::VisitOffsetOfExpr(const clang::OffsetOfExpr *OOE) {
  size_t Offset = 0;
  clang::QualType QT;
  for (unsigned int i = 0; i < OOE->getNumComponents(); i++) {
    const clang::OffsetOfNode &OON = OOE->getComponent(i);
    switch (OON.getKind()) {
    case clang::OffsetOfNode::Field: {
      clang::FieldDecl *Field = OON.getField();
      QT = Field->getType();
      uint64_t FieldOffset = Context->getFieldOffset(Field) / BITS_PER_BYTE;
      Offset += FieldOffset;
    } break;
    case clang::OffsetOfNode::Array: {
      const clang::Expr *E = OOE->getIndexExpr(OON.getArrayExprIndex());
      clang::Expr::EvalResult IndexVal;
      if (E->EvaluateAsInt(IndexVal, *Context)) {
        llvm::APSInt Index = IndexVal.Val.getInt();
        const clang::Type *ElementType = QT->getPointeeOrArrayElementType();
        clang::CharUnits ElementSize = Context->getTypeSizeInChars(ElementType);
        Offset += (Index.getExtValue() * ElementSize.getQuantity());
      } else {
        ASSERT(false, "Non-constant array index in offsetof expressions");
      }
    } break;
    default:
      OOE->dump();
      ASSERT(false, "Unsupported offsetof");
    }
  }

  return Result(Builder->CreateIntConst(Offset, PTY_u64), OOE->getID(*Context),
                OOE->getOperatorLoc(), type2Mpl(OOE->getType()));
}

Result
Clang2MapleVisitor::VisitOpaqueValueExpr(const clang::OpaqueValueExpr *OVE) {
  MIRType *Ty = type2Mpl(OVE->getType());
  Result SourceRes = Visit(OVE->getSourceExpr());
  std::string Name = "_opaque." + std::to_string(OVE->getID(*Context));
  MIRSymbol *Temp = Builder->GetLocalDecl(Name);
  if (!Temp) {
    Temp = Builder->GetOrCreateLocalDecl(Name, *Ty);
    StmtNode *TempAssign =
        Builder->CreateStmtDassign(*Temp, 0, getNodeAsRVal(SourceRes));
    TempAssign->SetSrcPos(sourceLocation2Mpl(OVE->getBeginLoc()));
    Result Res(Temp, Ty, OVE->getBeginLoc());
    Res.appendStmts(SourceRes);
    Res.appendStmtBefore(TempAssign);
    return Res;
  }

  Result Res(Temp, Ty, OVE->getBeginLoc());
  Res.appendStmts(SourceRes);
  return Res;
}

Result Clang2MapleVisitor::VisitParenExpr(const clang::ParenExpr *PE) {
  return Visit(PE->getSubExpr());
}

Result
Clang2MapleVisitor::VisitPredefinedExpr(const clang::PredefinedExpr *PE) {
  return Visit(PE->getFunctionName());
}

// FIXME: This needs to be properly implemented
Result Clang2MapleVisitor::VisitShuffleVectorExpr(
    const clang::ShuffleVectorExpr *SVE) {
  return Visit(SVE->getExpr(0));
}

Result Clang2MapleVisitor::VisitStmtExpr(const clang::StmtExpr *SE) {
  Result Res(SE->getBeginLoc());
  const clang::CompoundStmt *CS = SE->getSubStmt();

  // All of the statements from the compound statement should run before the
  // last expression, which should be returned from this visitor.
  for (clang::Stmt *S : CS->body()) {
    // Do not include the last statement.
    if (S == CS->body_back())
      break;

    Result SRes = Visit(S);
    appendResultBefore(Res, SRes);
  }

  // Now add the last expression as the node returned for this expression.
  Result LastRes = Visit(CS->body_back());
  Res.appendStmts(LastRes);
  Res.setResult(LastRes);
  return Res;
}

Result Clang2MapleVisitor::VisitStringLiteral(const clang::StringLiteral *Lit) {
  MIRType *Ty = type2Mpl(Lit->getType());
  ConstvalNode *ConstVal = nullptr;

  if (Lit->isAscii()) {
    UStrIdx StrIdx = GlobalTables::GetUStrTable().GetOrCreateStrIdxFromName(
        Lit->getString().str());
    ConststrNode *Const =
        Module->GetMemPool()->New<ConststrNode>(PointerPrimTy, StrIdx);
    return Result(Const, Lit->getID(*Context), Lit->getBeginLoc(), Ty);
  } else if (Lit->isWide()) {
    ConstVal = Module->CurFuncCodeMemPool()->New<ConstvalNode>();
    MIRAggConst *Const =
        Module->CurFuncCodeMemPool()->New<MIRAggConst>(*Module, *Ty);
    for (unsigned int i = 0; i < Lit->getLength(); i++) {
      Const->PushBack(GlobalTables::GetIntConstTable().GetOrCreateIntConst(
          Lit->getCodeUnit(i),
          *GlobalTables::GetTypeTable().GetPrimType(PTY_i32)));
    }
    ConstVal->SetPrimType(PTY_agg);
    ConstVal->SetConstVal(Const);
  } else {
    Lit->dump();
    ASSERT(false, "Unsupported string type");
  }

  return Result(ConstVal, Lit->getID(*Context), Lit->getBeginLoc(), Ty);
}

Result Clang2MapleVisitor::VisitTypeTraitExpr(const clang::TypeTraitExpr *TTE) {
  MIRType *Ty = type2Mpl(TTE->getType());
  return Result(Builder->CreateIntConst(TTE->getValue() ? 1 : 0, PTY_i32),
                TTE->getID(*Context), TTE->getBeginLoc(), Ty);
}

Result Clang2MapleVisitor::VisitUnaryExprOrTypeTraitExpr(
    const clang::UnaryExprOrTypeTraitExpr *UETT) {
  MIRType *Ty = type2Mpl(UETT->getType());
  clang::QualType QT;
  if (UETT->isArgumentType()) {
    QT = UETT->getArgumentType();
  } else {
    QT = UETT->getArgumentExpr()->getType();
  }

  switch (UETT->getKind()) {
  case clang::UETT_SizeOf: {
    // C11 specification: ISO/IEC 9899:201x
    // $ 6.5.3.4
    // "If the type of the operand is a variable length array type,
    // the operand is evaluated; otherwise, the operand is not evaluated
    // and the result is an integer constant."
    if (QT->isVariableArrayType()) {
      return buildExprToComputeSizeFromVLA(UETT, QT);
    }
    clang::CharUnits Size = Context->getTypeSizeInChars(QT);
    return Result(
        Builder->CreateIntConst(Size.getQuantity(), Ty->GetPrimType()),
        UETT->getID(*Context), UETT->getBeginLoc(), Ty);
  } break;
  case clang::UETT_AlignOf:
  case clang::UETT_PreferredAlignOf: {
    clang::CharUnits Align = Context->getTypeAlignInChars(QT);
    return Result(
        Builder->CreateIntConst(Align.getQuantity(), Ty->GetPrimType()),
        UETT->getID(*Context), UETT->getRParenLoc(), Ty);
  } break;
  default:
    LogInfo::MapleLogger()
        << "Error: Unhandled kind of UnaryExprOrTypeTraitExpr: "
        << UETT->getKind() << "\n";
    CHECK_FATAL(false, "Failing due to unsupported feature");
    return Result(UETT->getBeginLoc());
  }
}

Result Clang2MapleVisitor::VisitUnaryOperator(const clang::UnaryOperator *UO) {
  Result SubExpr = Visit(UO->getSubExpr());

  MIRType *ResTy = type2Mpl(UO->getType());

  BaseNode *UONode = nullptr;
  switch (UO->getOpcode()) {
  case clang::UO_Deref: {
    // Deref has to be handled by the parent. If it is on the left-hand side of
    // an assignment, then the assignment becomes an iassign, but if it is on
    // the right, then it needs an iread.
    MIRType *SubTy = type2Mpl(UO->getSubExpr()->getType());
    Result Res(getNodeAsRVal(SubExpr), UO->getID(*Context), SubTy, ResTy,
               UO->getExprLoc());
    Res.appendStmts(SubExpr);
    return Res;
  }
  case clang::UO_AddrOf:
    UONode = getNodeAsAddrOf(SubExpr);
    break;
  case clang::UO_Extension:
    // This is just a wrapper to indicate a GNU extension.
    return SubExpr;
  case clang::UO_Plus:
    return SubExpr;
  case clang::UO_Minus:
    UONode = Builder->CreateExprUnary(OP_neg, *ResTy, getNodeAsRVal(SubExpr));
    break;
  case clang::UO_Not:
    UONode = Builder->CreateExprUnary(OP_bnot, *ResTy, getNodeAsRVal(SubExpr));
    break;
  case clang::UO_LNot:
    UONode = exprToNotCond(getNodeAsRVal(SubExpr));
    break;
  case clang::UO_PostInc:
  case clang::UO_PostDec:
  case clang::UO_PreInc:
  case clang::UO_PreDec: {
    size_t IncAmount = 1;
    if (ResTy->GetKind() == kTypePointer) {
      IncAmount = static_cast<MIRPtrType *>(ResTy)->GetPointedType()->GetSize();
    }

    PrimitiveType ResPrimTy(ResTy->GetPrimType());
    if (ResPrimTy.IsInteger() && !ResPrimTy.IsAddress()) {
      ResTy = GlobalTables::GetTypeTable().GetPrimType(
          GetRegPrimType(ResTy->GetPrimType()));
    }
    BaseNode *IncAmountNode;
    if (PrimitiveType(ResTy->GetPrimType()).IsInteger()) {
      IncAmountNode =
          Builder->CreateIntConst(IncAmount, (ResTy->GetKind() == kTypePointer)
                                                 ? IntPointerTy->GetPrimType()
                                                 : ResTy->GetPrimType());
    } else if (ResTy->GetPrimType() == PTY_f32) {
      IncAmountNode = Builder->CreateFloatConst(1.0f);
    } else if (ResTy->GetPrimType() == PTY_f64) {
      IncAmountNode = Builder->CreateDoubleConst(1.0);
    } else {
      ResTy->Dump(0);
      ASSERT(false, "Unexpected type of pre/post inc/dec");
    }
    BaseNode *BinNode =
        Builder->CreateExprBinary(UO->isDecrementOp() ? OP_sub : OP_add, *ResTy,
                                  getNodeAsRVal(SubExpr), IncAmountNode);

    if (UO->isPrefix()) {
      Result Res(getNodeAsRVal(SubExpr), UO->getID(*Context), UO->getExprLoc(),
                 ResTy, TypeAttrs(), true);
      Res.appendStmts(SubExpr);
      StmtNode *Assign = nullptr;
      if (SubExpr.isSym()) {
        Assign = Builder->CreateStmtDassign(*SubExpr.getSym(),
                                            SubExpr.getField(), BinNode);
      } else if (SubExpr.isDeref()) {
        Assign =
            Builder->CreateStmtIassign(*SubExpr.getAddrTy(), SubExpr.getField(),
                                       SubExpr.getAddr(), BinNode);
      } else {
        LogInfo::MapleLogger()
            << "Error: Unhandled expression in prefix operation\n";
#ifdef DEBUG
        getNodeAsRVal(SubExpr)->Dump();
#endif
        CHECK_FATAL(false, "Failing due to unsupported feature");
        return Result(UO->getExprLoc());
      }
      Assign->SetSrcPos(sourceLocation2Mpl(UO->getExprLoc()));
      Res.appendStmtBefore(Assign);
      return Res;
    } else {
      MIRSymbol *Temp = Builder->GetOrCreateLocalDecl(
          "post." + std::to_string(UO->getID(*Context)), *ResTy);
      Result Res(Temp, ResTy, UO->getExprLoc(), TypeAttrs(), true);
      Res.appendStmts(SubExpr);
      StmtNode *TempAssign =
          Builder->CreateStmtDassign(*Temp, 0, getNodeAsRVal(SubExpr));
      TempAssign->SetSrcPos(sourceLocation2Mpl(UO->getExprLoc()));
      Res.appendStmtBefore(TempAssign);
      StmtNode *Assign = nullptr;
      if (SubExpr.isSym()) {
        Assign = Builder->CreateStmtDassign(*SubExpr.getSym(),
                                            SubExpr.getField(), BinNode);
      } else if (SubExpr.isDeref()) {
        Assign =
            Builder->CreateStmtIassign(*SubExpr.getAddrTy(), SubExpr.getField(),
                                       SubExpr.getAddr(), BinNode);
      } else {
        LogInfo::MapleLogger()
            << "Error: Unhandled expression in postfix operation\n";
#ifdef DEBUG
        getNodeAsRVal(SubExpr)->Dump();
#endif
        CHECK_FATAL(false, "Failing due to unsupported feature");
      }

      Assign->SetSrcPos(sourceLocation2Mpl(UO->getExprLoc()));
      Res.appendStmtBefore(Assign);
      return Res;
    }
  }
  default:
    LogInfo::MapleLogger() << "Error: Unhandled unary opcode\n";
#ifdef DEBUG
    UO->dump();
#endif
    CHECK_FATAL(false, "Failing due to unsupported feature");
  }

  Result Res(UONode, UO->getID(*Context), UO->getExprLoc(), ResTy);
  Res.appendStmts(SubExpr);
  return Res;
}

BaseNode *Clang2MapleVisitor::readField(Result BaseRes, FieldID Field,
                                        MIRType *Ty) {
  if (BaseRes.isSym()) {
    return Builder->CreateExprDread(*Ty, BaseRes.getField() + Field,
                                    *BaseRes.getSym());
  } else {
    return Builder->CreateExprIread(*Ty, *BaseRes.getAddrTy(),
                                    BaseRes.getField() + Field,
                                    BaseRes.getNode());
  }
}

StmtNode *Clang2MapleVisitor::writeField(Result BaseRes, FieldID Field,
                                         BaseNode *Src) {
  if (BaseRes.isSym()) {
    return Builder->CreateStmtDassign(*BaseRes.getSym(),
                                      BaseRes.getField() + Field, Src);
  } else {
    return Builder->CreateStmtIassign(*BaseRes.getAddrTy(),
                                      BaseRes.getField() + Field,
                                      BaseRes.getNode(), Src);
  }
}

Result Clang2MapleVisitor::VisitVAArgExpr(const clang::VAArgExpr *VAArg) {
  ASSERT(Context->getTargetInfo().getBuiltinVaListKind() ==
             clang::TargetInfo::AArch64ABIBuiltinVaList,
         "Architecture not yet supported for varargs");
  // This sequence of instructions implements the pseducode from
  // https://developer.arm.com/documentation/ihi0055/d/?lang=en#the-va-arg-macro
  MIRType *ArgTy = type2Mpl(VAArg->getType());
  MIRPtrType *PointerToArgTy = static_cast<MIRPtrType *>(
      GlobalTables::GetTypeTable().GetOrCreatePointerType(*ArgTy,
                                                          PointerPrimTy));
  SrcPosition SrcPos = sourceLocation2Mpl(VAArg->getBeginLoc());

  MIRType *IntTy =
      GlobalTables::GetTypeTable().GetTypeFromTyIdx(TyIdx(PTY_i32));
  MIRSymbol *Offs = Builder->GetOrCreateLocalDecl(
      "va_offs." + std::to_string(++UnnamedCount), *IntTy);
  MIRSymbol *ArgPtr = Builder->GetOrCreateLocalDecl(
      "va_arg_ptr." + std::to_string(++UnnamedCount), *PointerToArgTy);

  Result Res(VAArg->getBeginLoc());

  Result ApRes = Visit(VAArg->getSubExpr());
  Res.appendStmts(ApRes);
  MIRStructType *ApListTy =
      static_cast<MIRStructType *>(type2Mpl(VAArg->getSubExpr()->getType()));

  LabelIdx OnStackLabel = Builder->GetOrCreateMIRLabel(
      "_vaarg_on_stack." + std::to_string(++UnnamedCount));
  LabelIdx EndLabel = Builder->GetOrCreateMIRLabel(
      "_vaarg_end." + std::to_string(++UnnamedCount));

  // If the argument type is a Composite Type that is larger than 16 bytes,
  // then the argument is copied to memory allocated by the caller and the
  // argument is replaced by a pointer to the copy.
  bool CopyToStack = false;
  if (ArgTy->IsStructType() && ArgTy->GetSize() > 16) {
    CopyToStack = true;
    ArgTy = PointerToArgTy;
    PointerToArgTy = static_cast<MIRPtrType *>(
        GlobalTables::GetTypeTable().GetOrCreatePointerType(*ArgTy));
  }

  // if (type passed in general registers)
  if (passInGeneralRegisters(ArgTy)) {
    // offs = ap.__gr_offs
    StmtNode *S =
        Builder->CreateStmtDassign(*Offs, 0, readField(ApRes, 4, IntTy));
    S->SetSrcPos(SrcPos);
    Res.appendStmtBefore(S);

    // if (offs >= 0), goto on_stack
    ConstvalNode *ZeroNode = Builder->GetConstInt(0);
    BaseNode *ReadOffs = Builder->CreateExprDread(*IntTy, 0, *Offs);
    BaseNode *Cond =
        Builder->CreateExprCompare(OP_ge, *IntTy, *IntTy, ReadOffs, ZeroNode);
    StmtNode *CondBranch =
        Builder->CreateStmtCondGoto(Cond, OP_brtrue, OnStackLabel);
    CondBranch->SetSrcPos(SrcPos);
    Res.appendStmtBefore(CondBranch);

    // if (alignof(type) > 8) offs = (offs + 15) & -16;
    if (ArgTy->GetAlign() > 8) {
      BaseNode *SumNode = Builder->CreateExprBinary(
          OP_add, *IntTy, Builder->CreateExprDread(*IntTy, 0, *Offs),
          Builder->GetConstInt(15));
      BaseNode *AndNode = Builder->CreateExprBinary(OP_band, *IntTy, SumNode,
                                                    Builder->GetConstInt(-16));
      StmtNode *Assign = Builder->CreateStmtDassign(*Offs, 0, AndNode);
      Assign->SetSrcPos(SrcPos);
      Res.appendStmtBefore(Assign);
    }

    // nreg = (sizeof(type) + 7) / 8;
    int Nreg = (ArgTy->GetSize() + 7) / 8;

    // ap.__gr_offs = offs + (nreg * 8);
    BaseNode *OffsSum = Builder->CreateExprBinary(
        OP_add, *IntTy, Builder->CreateExprDread(*IntTy, 0, *Offs),
        Builder->GetConstInt(Nreg * 8));
    StmtNode *UpdateOffs = writeField(ApRes, 4, OffsSum);
    UpdateOffs->SetSrcPos(SrcPos);
    Res.appendStmtBefore(UpdateOffs);

    // if (ap.__gr_offs > 0) goto on_stack;
    ZeroNode = Builder->GetConstInt(0);
    ReadOffs = readField(ApRes, 4, IntTy);
    Cond =
        Builder->CreateExprCompare(OP_gt, *IntTy, *IntTy, ReadOffs, ZeroNode);
    CondBranch = Builder->CreateStmtCondGoto(Cond, OP_brtrue, OnStackLabel);
    CondBranch->SetSrcPos(SrcPos);
    Res.appendStmtBefore(CondBranch);

    // return *(type *)(ap.__gr_top + offs);
    BaseNode *ArgAddr = Builder->CreateExprBinary(
        OP_add, *ApListTy->GetFieldType(2),
        readField(ApRes, 2, ApListTy->GetFieldType(2)),
        Builder->CreateExprTypeCvt(OP_cvt, *ApListTy->GetFieldType(2), *IntTy,
                                   Builder->CreateExprDread(*IntTy, 0, *Offs)));
    StmtNode *AssignPtr = Builder->CreateStmtDassign(*ArgPtr, 0, ArgAddr);
    AssignPtr->SetSrcPos(SrcPos);
    Res.appendStmtBefore(AssignPtr);

    GotoNode *GoToEnd = Builder->CreateStmtGoto(OP_goto, EndLabel);
    GoToEnd->SetSrcPos(SrcPos);
    Res.appendStmtBefore(GoToEnd);
  }
  // else if (type is an HFA or an HVA)
  // TODO: Handle HVA
  else if (MIRType *ElemTy = isHomogenousAggregate(ArgTy)) {
    // offs = ap.__vr_offs
    StmtNode *S =
        Builder->CreateStmtDassign(*Offs, 0, readField(ApRes, 5, IntTy));
    S->SetSrcPos(SrcPos);
    Res.appendStmtBefore(S);

    // if (offs >= 0), goto on_stack
    ConstvalNode *ZeroNode = Builder->GetConstInt(0);
    BaseNode *ReadOffs = Builder->CreateExprDread(*IntTy, 0, *Offs);
    BaseNode *Cond =
        Builder->CreateExprCompare(OP_ge, *IntTy, *IntTy, ReadOffs, ZeroNode);
    StmtNode *CondBranch =
        Builder->CreateStmtCondGoto(Cond, OP_brtrue, OnStackLabel);
    CondBranch->SetSrcPos(SrcPos);
    Res.appendStmtBefore(CondBranch);

    // nreg = sizeof(type) / sizeof(ftype);
    int Nreg = ArgTy->GetSize() / ElemTy->GetSize();
    // ap.__vr_offs = offs + (nreg * 16);
    BaseNode *OffsSum = Builder->CreateExprBinary(
        OP_add, *IntTy, Builder->CreateExprDread(*IntTy, 0, *Offs),
        Builder->GetConstInt(Nreg * 16));
    StmtNode *UpdateOffs = writeField(ApRes, 5, OffsSum);
    UpdateOffs->SetSrcPos(SrcPos);
    Res.appendStmtBefore(UpdateOffs);

    // if (ap.__vr_offs > 0) goto on_stack;
    ZeroNode = Builder->GetConstInt(0);
    ReadOffs = readField(ApRes, 5, IntTy);
    Cond =
        Builder->CreateExprCompare(OP_gt, *IntTy, *IntTy, ReadOffs, ZeroNode);
    CondBranch = Builder->CreateStmtCondGoto(Cond, OP_brtrue, OnStackLabel);
    CondBranch->SetSrcPos(SrcPos);
    Res.appendStmtBefore(CondBranch);

    // for (i = 0; i < nreg; i++, offs += 16)
    //     ha.field[i] = *((ftype *)(ap.__vr_top + offs));
    // return ha;
    BaseNode *ArgAddr = Builder->CreateExprBinary(
        OP_add, *ApListTy->GetFieldType(2),
        readField(ApRes, 3, ApListTy->GetFieldType(3)),
        Builder->CreateExprTypeCvt(OP_cvt, *ApListTy->GetFieldType(3), *IntTy,
                                   Builder->CreateExprDread(*IntTy, 0, *Offs)));
    StmtNode *AssignPtr = Builder->CreateStmtDassign(*ArgPtr, 0, ArgAddr);
    AssignPtr->SetSrcPos(SrcPos);
    Res.appendStmtBefore(AssignPtr);

    MIRStructType *StructTy = static_cast<MIRStructType *>(ArgTy);
    MIRSymbol *Ha = Builder->CreateLocalDecl(
        "vaarg_ha." + std::to_string(++UnnamedCount), *StructTy);
    unsigned SrcOffset = 0;
    for (size_t FID = 1; FID <= StructTy->NumberOfFieldIDs(); FID++) {
      MIRType *FieldTy = StructTy->GetFieldType(FID);
      MIRType *PointerToFieldTy =
          GlobalTables::GetTypeTable().GetOrCreatePointerType(*FieldTy,
                                                              PointerPrimTy);
      if (FieldTy->IsStructType()) {
        continue;
      }

      if (FieldTy->GetKind() == kTypeArray) {
        MIRArrayType *ArrayTy = static_cast<MIRArrayType *>(FieldTy);
        MIRPtrType *PointerToElemTy =
            static_cast<MIRPtrType*>(GlobalTables::GetTypeTable().GetOrCreatePointerType(*ElemTy, PointerPrimTy));
        for (size_t ElemID = 0; ElemID < ArrayTy->GetSizeArrayItem(0);
             ElemID++) {
          BaseNode *BaseAddr = Builder->CreateExprAddrof(FID, *Ha);
          BaseAddr->SetPrimType(PointerPrimTy);
          BaseNode *IndexNode =
              Builder->CreateIntConst(ElemID, IntPointerTy->GetPrimType());
          ArrayNode *Array =
              Builder->CreateExprArray(*ArrayTy, BaseAddr, IndexNode);
          Array->SetBoundsCheck(false);
          Array->SetPrimType(PointerPrimTy);
          StmtNode *AssignElem = Builder->CreateStmtIassign(
              *PointerToElemTy, 0, Array,
              Builder->CreateExprIread(
                  *PointerToElemTy->GetPointedType(), *PointerToElemTy, 0,
                  Builder->CreateExprBinary(
                      OP_add, *PointerToElemTy,
                      Builder->CreateExprDread(*PointerToArgTy, *ArgPtr),
                      Builder->CreateIntConst(SrcOffset, PointerPrimTy))));
          AssignElem->SetSrcPos(SrcPos);
          Res.appendStmtBefore(AssignElem);

          SrcOffset += 16;
        }
      } else {
        StmtNode *AssignField = Builder->CreateStmtDassign(
            *Ha, FID,
            Builder->CreateExprIread(
                *FieldTy, *PointerToFieldTy, 0,
                Builder->CreateExprBinary(
                    OP_add, *PointerToFieldTy,
                    Builder->CreateExprDread(*PointerToArgTy, *ArgPtr),
                    Builder->CreateIntConst(SrcOffset, PointerPrimTy))));
        AssignField->SetSrcPos(SrcPos);
        Res.appendStmtBefore(AssignField);

        SrcOffset += 16;
      }
    }

    AssignPtr = Builder->CreateStmtDassign(*ArgPtr, 0,
                                           Builder->CreateExprAddrof(0, *Ha));
    AssignPtr->SetSrcPos(SrcPos);
    Res.appendStmtBefore(AssignPtr);

    GotoNode *GoToEnd = Builder->CreateStmtGoto(OP_goto, EndLabel);
    GoToEnd->SetSrcPos(SrcPos);
    Res.appendStmtBefore(GoToEnd);
  }
  // else if (type passed in fp/simd registers)
  // TODO: handle simd registers
  else if (passInFloatRegisters(ArgTy)) {
    // offs = ap.__vr_offs
    StmtNode *S =
        Builder->CreateStmtDassign(*Offs, 0, readField(ApRes, 5, IntTy));
    S->SetSrcPos(SrcPos);
    Res.appendStmtBefore(S);

    // if (offs >= 0), goto on_stack
    ConstvalNode *ZeroNode = Builder->GetConstInt(0);
    BaseNode *ReadOffs = Builder->CreateExprDread(*IntTy, 0, *Offs);
    BaseNode *Cond =
        Builder->CreateExprCompare(OP_ge, *IntTy, *IntTy, ReadOffs, ZeroNode);
    StmtNode *CondBranch =
        Builder->CreateStmtCondGoto(Cond, OP_brtrue, OnStackLabel);
    CondBranch->SetSrcPos(SrcPos);
    Res.appendStmtBefore(CondBranch);

    // nreg = (sizeof(type) + 15) / 16;
    // Note that we use 7, 8 here instead of 15, 16. This is because the
    // sizeof a double is 8, but for varargs, it uses 16.
    int Nreg = (ArgTy->GetSize() + 7) / 8;
    // ap.__vr_offs = offs + (nreg * 16);
    BaseNode *OffsSum = Builder->CreateExprBinary(
        OP_add, *IntTy, Builder->CreateExprDread(*IntTy, 0, *Offs),
        Builder->GetConstInt(Nreg * 16));
    StmtNode *UpdateOffs = writeField(ApRes, 5, OffsSum);
    UpdateOffs->SetSrcPos(SrcPos);
    Res.appendStmtBefore(UpdateOffs);

    // if (ap.__vr_offs > 0) goto on_stack;
    ZeroNode = Builder->GetConstInt(0);
    ReadOffs = readField(ApRes, 5, IntTy);
    Cond =
        Builder->CreateExprCompare(OP_gt, *IntTy, *IntTy, ReadOffs, ZeroNode);
    CondBranch = Builder->CreateStmtCondGoto(Cond, OP_brtrue, OnStackLabel);
    CondBranch->SetSrcPos(SrcPos);
    Res.appendStmtBefore(CondBranch);

    // return *(type *)(ap.__vr_top + offs);
    BaseNode *ArgAddr = Builder->CreateExprBinary(
        OP_add, *ApListTy->GetFieldType(2),
        readField(ApRes, 3, ApListTy->GetFieldType(3)),
        Builder->CreateExprTypeCvt(OP_cvt, *ApListTy->GetFieldType(3), *IntTy,
                                   Builder->CreateExprDread(*IntTy, 0, *Offs)));
    StmtNode *AssignPtr = Builder->CreateStmtDassign(*ArgPtr, 0, ArgAddr);
    AssignPtr->SetSrcPos(SrcPos);
    Res.appendStmtBefore(AssignPtr);

    GotoNode *GoToEnd = Builder->CreateStmtGoto(OP_goto, EndLabel);
    GoToEnd->SetSrcPos(SrcPos);
    Res.appendStmtBefore(GoToEnd);
  }

  // on_stack:
  StmtNode *LabelStmt = Builder->CreateStmtLabel(OnStackLabel);
  LabelStmt->SetSrcPos(SrcPos);
  Res.appendStmtBefore(LabelStmt);

  // intptr_t arg = ap.__stack;
  StmtNode *StackBase =
      Builder->CreateStmtDassign(*ArgPtr, 0, readField(ApRes, 1, IntPointerTy));
  StackBase->SetSrcPos(SrcPos);
  Res.appendStmtBefore(StackBase);

  // if (alignof(type) > 8) arg = (arg + 15) & -16;
  if (ArgTy->GetAlign() > 8) {
    BaseNode *SumNode = Builder->CreateExprBinary(
        OP_add, *IntPointerTy,
        Builder->CreateExprDread(*IntPointerTy, 0, *ArgPtr),
        Builder->GetConstInt(15));
    BaseNode *AndNode = Builder->CreateExprBinary(
        OP_band, *IntPointerTy, SumNode, Builder->GetConstInt(-16));
    StmtNode *Assign = Builder->CreateStmtDassign(*ArgPtr, 0, AndNode);
    Assign->SetSrcPos(SrcPos);
    Res.appendStmtBefore(Assign);
  }

  // ap.__stack = (void *)((arg + sizeof(type) + 7) & -8);
  BaseNode *CalcArg = Builder->CreateExprBinary(
      OP_add, *IntPointerTy,
      Builder->CreateExprDread(*IntPointerTy, 0, *ArgPtr),
      Builder->CreateIntConst(ArgTy->GetSize() + 7,
                              IntPointerTy->GetPrimType()));
  CalcArg = Builder->CreateExprBinary(
      OP_band, *IntPointerTy, CalcArg,
      Builder->CreateIntConst(-8, IntPointerTy->GetPrimType()));
  StmtNode *StackAssign = writeField(ApRes, 1, CalcArg);
  StackAssign->SetSrcPos(SrcPos);
  Res.appendStmtBefore(StackAssign);

  // return *(type *)arg;
  // end:
  LabelStmt = Builder->CreateStmtLabel(EndLabel);
  LabelStmt->SetSrcPos(SrcPos);
  Res.appendStmtBefore(LabelStmt);

  BaseNode *Arg = Builder->CreateExprIread(
      *ArgTy, *PointerToArgTy, 0,
      Builder->CreateExprDread(*PointerToArgTy, 0, *ArgPtr));

  // If the argument type is a Composite Type that is larger than 16 bytes,
  // then the argument is copied to memory allocated by the caller and the
  // argument is replaced by a pointer to the copy.
  if (CopyToStack) {
    PointerToArgTy = static_cast<MIRPtrType *>(ArgTy);
    ArgTy = PointerToArgTy->GetPointedType();
    Arg = Builder->CreateExprIread(*ArgTy, *PointerToArgTy, 0, Arg);
  }

  Res.setNode(Arg, VAArg->getID(*Context), ArgTy);
  return Res;
}

//
// Private methods
//

void Clang2MapleVisitor::setupBuiltinTypes(void) {
  std::map<const clang::Type *, TyIdx> BuiltinTypes = {
      {Context->BoolTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetUInt8()->GetTypeIndex()},
      {Context->CharTy.getTypePtr(),
       Context->CharTy->isSignedIntegerType()
           ? GlobalTables::GetTypeTable().GetInt8()->GetTypeIndex()
           : GlobalTables::GetTypeTable().GetUInt8()->GetTypeIndex()},
      {Context->SignedCharTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetInt8()->GetTypeIndex()},
      {Context->ShortTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetInt16()->GetTypeIndex()},
      {Context->IntTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetInt32()->GetTypeIndex()},
      {Context->LongLongTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetInt64()->GetTypeIndex()},
      // __int128 is handled as an i64
      {Context->Int128Ty.getTypePtr(),
       GlobalTables::GetTypeTable().GetInt64()->GetTypeIndex()},
      {Context->UnsignedCharTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetUInt8()->GetTypeIndex()},
      {Context->UnsignedShortTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetUInt16()->GetTypeIndex()},
      {Context->UnsignedIntTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetUInt32()->GetTypeIndex()},
      {Context->UnsignedLongLongTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetUInt64()->GetTypeIndex()},
      // unsigned __int128 is handled as a u64
      {Context->UnsignedInt128Ty.getTypePtr(),
       GlobalTables::GetTypeTable().GetUInt64()->GetTypeIndex()},
      // __fp16 is handled as a float
      {Context->HalfTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetFloat()->GetTypeIndex()},
      {Context->FloatTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetFloat()->GetTypeIndex()},
      {Context->DoubleTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetDouble()->GetTypeIndex()},
      // long double is handled as a double
      {Context->LongDoubleTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetDouble()->GetTypeIndex()},
      {Context->FloatComplexTy.getTypePtr(), TyIdx(PTY_c64)},
      {Context->DoubleComplexTy.getTypePtr(), TyIdx(PTY_c128)},
      {Context->VoidTy.getTypePtr(),
       GlobalTables::GetTypeTable().GetVoid()->GetTypeIndex()}};
  TypeMap.insert(BuiltinTypes.begin(), BuiltinTypes.end());
  if (Context->getTypeSize(Context->LongTy) == 32) {
    TypeMap[Context->LongTy.getTypePtr()] =
        GlobalTables::GetTypeTable().GetInt32()->GetTypeIndex();
    TypeMap[Context->UnsignedLongTy.getTypePtr()] =
        GlobalTables::GetTypeTable().GetUInt32()->GetTypeIndex();
    PointerPrimTy = PTY_a32;
    IntPointerTy = GlobalTables::GetTypeTable().GetUInt32();
  } else {
    TypeMap[Context->LongTy.getTypePtr()] =
        GlobalTables::GetTypeTable().GetInt64()->GetTypeIndex();
    TypeMap[Context->UnsignedLongTy.getTypePtr()] =
        GlobalTables::GetTypeTable().GetUInt64()->GetTypeIndex();
    PointerPrimTy = PTY_a64;
    IntPointerTy = GlobalTables::GetTypeTable().GetUInt64();
  }
}

TyIdx Clang2MapleVisitor::type2MplIdx(clang::QualType QT, bool needComplete) {
  const clang::Type *Ty = QT.getDesugaredType(*Context).getTypePtr();
  auto it = TypeMap.find(Ty);
  if (it != TypeMap.end()) {
    return it->second;
  }

  // Create a new function type
  if (const clang::FunctionType *FT = llvm::dyn_cast<clang::FunctionType>(Ty)) {
    TyIdx ReturnTy = type2MplIdx(FT->getReturnType());

    std::vector<TyIdx> ParamTypeList;
    std::vector<TypeAttrs> ParamAttrsList;
    bool IsVariadic = false;
    bool IsFirstArgRet = false;
    if (GlobalTables::GetTypeTable().GetTypeFromTyIdx(ReturnTy)->GetSize() >
        16) {
      MIRType *ReturnTyPtr =
          GlobalTables::GetTypeTable().GetOrCreatePointerType(ReturnTy,
                                                              PointerPrimTy);
      ParamTypeList.push_back(ReturnTyPtr->GetTypeIndex());

      IsFirstArgRet = true;

      TypeAttrs Attrs;
      if (isOneElementVector(FT->getReturnType())) {
        Attrs.SetAttr(ATTR_oneelem_simd);
      }

      // TODO: Add other attributes

      ParamAttrsList.push_back(Attrs);
      ReturnTy = GlobalTables::GetTypeTable().GetVoid()->GetTypeIndex();
    }

    if (const clang::FunctionProtoType *FPT =
            llvm::dyn_cast<clang::FunctionProtoType>(Ty)) {
      for (const clang::QualType &Param : FPT->param_types()) {
        ParamTypeList.push_back(type2MplIdx(Param));

        TypeAttrs Attrs;
        if (isOneElementVector(Param.getTypePtr())) {
          Attrs.SetAttr(ATTR_oneelem_simd);
        }

        // TODO: Add other attributes

        ParamAttrsList.push_back(Attrs);
      }

      IsVariadic = FPT->isVariadic();
    }

    MIRType *MTy = GlobalTables::GetTypeTable().GetOrCreateFunctionType(
        ReturnTy, ParamTypeList, ParamAttrsList, IsVariadic);
    if (IsFirstArgRet) {
      static_cast<MIRFuncType *>(MTy)->SetFirstArgReturn();
    }
    TypeMap.insert({FT, MTy->GetTypeIndex()});

    return MTy->GetTypeIndex();
  } else if (const clang::PointerType *PtrTy =
                 llvm::dyn_cast<clang::PointerType>(Ty)) {
    // Create a pointer type from the pointee type
    TyIdx PointeeTyIdx = type2MplIdx(PtrTy->getPointeeType(), false);
    MIRPtrType *MTy = static_cast<MIRPtrType *>(
        GlobalTables::GetTypeTable().GetOrCreatePointerType(PointeeTyIdx,
                                                            PointerPrimTy));
    MTy->SetPrimType(PointerPrimTy);

    // If this is a pointer to a type which is not yet fully-defined, record
    // it to be updated later.
    if (GlobalTables::GetTypeTable()
            .GetTypeFromTyIdx(PointeeTyIdx)
            ->GetKind() == kTypeStructIncomplete) {
      UnresolvedTypes.insert({PtrTy->getPointeeType()->getAsRecordDecl(), MTy});
    }

    TypeAttrs Attrs;
    // Get alignment from the pointee type
    if (unsigned int AlignmentBits =
            Context->getTypeAlignIfKnown(PtrTy->getPointeeType())) {
      if (AlignmentBits >
          Context->getTypeUnadjustedAlign(PtrTy->getPointeeType())) {
        Attrs.SetAlign(AlignmentBits / BITS_PER_BYTE);
      }
    }
    if (TypeHasMayAlias(PtrTy->getPointeeType())) {
      Attrs.SetAttr(ATTR_may_alias);
    }
    if (isOneElementVector(PtrTy->getPointeeType())) {
      Attrs.SetAttr(ATTR_oneelem_simd);
    }
    MTy->SetTypeAttrs(Attrs);

    TypeMap.insert({PtrTy, MTy->GetTypeIndex()});

    return MTy->GetTypeIndex();
  } else if (const clang::ReferenceType *RefTy =
                 llvm::dyn_cast<clang::ReferenceType>(Ty)) {
    TyIdx BaseTyIdx = type2MplIdx(RefTy->getPointeeType(), false);
    MIRType *MTy = GlobalTables::GetTypeTable().GetOrCreatePointerType(
        BaseTyIdx, PointerPrimTy);
    TypeMap.insert({PtrTy, MTy->GetTypeIndex()});

    return MTy->GetTypeIndex();
  } else if (const clang::ConstantArrayType *ArrTy =
                 llvm::dyn_cast<clang::ConstantArrayType>(Ty)) {
    // Create a constant array type based on a known type
#ifdef MULTIDIM_ARRAYS
    std::vector<maple::uint32> SizeArray;
    clang::QualType BaseTy = QT.getDesugaredType(*Context);
    while (BaseTy->isConstantArrayType()) {
      ArrTy = llvm::dyn_cast<clang::ConstantArrayType>(BaseTy);
      const llvm::APInt &APSize = ArrTy->getSize();
      uint32_t Size = (uint32_t)APSize.getSExtValue();
      SizeArray.push_back(Size);
      BaseTy = ArrTy->getElementType().getDesugaredType(*Context);
    }

    MIRArrayType ArrayTy(type2MplIdx(BaseTy), SizeArray);
#else  // !MULTIDIM_ARRAYS
    TyIdx BaseTyIdx = type2MplIdx(ArrTy->getElementType());
    const llvm::APInt &APSize = ArrTy->getSize();
    uint32_t Size = (uint32_t)APSize.getSExtValue();

    std::vector<maple::uint32> SizeArray{Size};

    MIRArrayType ArrayTy(BaseTyIdx, SizeArray);
#endif // MULTIDIM_ARRAYS
    MIRArrayType *MTy = static_cast<MIRArrayType *>(
        GlobalTables::GetTypeTable().GetOrCreateMIRTypeNode(ArrayTy));

    TypeAttrs Attrs = MTy->GetTypeAttrs();
    // Get alignment from the element type
    if (unsigned int AlignmentBits =
            Context->getTypeAlignIfKnown(ArrTy->getElementType())) {
      if (AlignmentBits >
          Context->getTypeUnadjustedAlign(ArrTy->getElementType())) {
        Attrs.SetAlign(AlignmentBits / BITS_PER_BYTE);
      }
    }
    if (isOneElementVector(ArrTy->getElementType())) {
      Attrs.SetAttr(ATTR_oneelem_simd);
    }

    MTy->SetTypeAttrs(Attrs);

    TypeMap.insert({Ty, MTy->GetTypeIndex()});

    return MTy->GetTypeIndex();
  } else if (const clang::VariableArrayType *VarArrTy =
                 llvm::dyn_cast<clang::VariableArrayType>(Ty)) {
    // Convert this to a pointer to the element type
    TyIdx BaseTyIdx = type2MplIdx(VarArrTy->getElementType());

    MIRPtrType *MTy = static_cast<MIRPtrType *>(
        GlobalTables::GetTypeTable().GetOrCreatePointerType(BaseTyIdx,
                                                            PointerPrimTy));
    MTy->SetPrimType(PointerPrimTy);

    TypeAttrs Attrs;

    // Get alignment from the element type
    if (unsigned int AlignmentBits =
            Context->getTypeAlignIfKnown(VarArrTy->getElementType())) {
      if (AlignmentBits >
          Context->getTypeUnadjustedAlign(VarArrTy->getElementType())) {
        Attrs.SetAlign(AlignmentBits / BITS_PER_BYTE);
      }
    }
    if (isOneElementVector(VarArrTy->getElementType())) {
      Attrs.SetAttr(ATTR_oneelem_simd);
    }

    MTy->SetTypeAttrs(Attrs);

    TypeMap.insert({VarArrTy, MTy->GetTypeIndex()});

    return MTy->GetTypeIndex();
  } else if (const clang::IncompleteArrayType *IncArrTy =
                 llvm::dyn_cast<clang::IncompleteArrayType>(Ty)) {
    // For an incomplete array type, assume a length of 1
#ifdef MULTIDIM_ARRAYS
    std::vector<maple::uint32> SizeArray{1};
    clang::QualType BaseTy =
        IncArrTy->getElementType().getDesugaredType(*Context);
    while (BaseTy->isConstantArrayType()) {
      ArrTy = llvm::dyn_cast<clang::ConstantArrayType>(BaseTy);
      const llvm::APInt &APSize = ArrTy->getSize();
      uint32_t Size = (uint32_t)APSize.getSExtValue();
      SizeArray.push_back(Size);
      BaseTy = ArrTy->getElementType().getDesugaredType(*Context);
    }
    TyIdx BaseTyIdx = type2MplIdx(BaseTy);
#else  // !MULTIDIM_ARRAYS
    TyIdx BaseTyIdx = type2MplIdx(IncArrTy->getElementType());
    std::vector<maple::uint32> SizeArray{1};
#endif // MULTIDIM_ARRAYS

    MIRArrayType ArrayTy(BaseTyIdx, SizeArray);
    MIRArrayType *MTy = static_cast<MIRArrayType *>(
        GlobalTables::GetTypeTable().GetOrCreateMIRTypeNode(ArrayTy));

    TypeAttrs Attrs = MTy->GetTypeAttrs();
    // Get alignment from the element type
    if (unsigned int AlignmentBits =
            Context->getTypeAlignIfKnown(IncArrTy->getElementType())) {
      if (AlignmentBits >
          Context->getTypeUnadjustedAlign(IncArrTy->getElementType())) {
        Attrs.SetAlign(AlignmentBits / BITS_PER_BYTE);
      }
    }
    if (isOneElementVector(IncArrTy->getElementType())) {
      Attrs.SetAttr(ATTR_oneelem_simd);
    }

    MTy->SetTypeAttrs(Attrs);

    TypeMap.insert({IncArrTy, MTy->GetTypeIndex()});

    return MTy->GetTypeIndex();
  } else if (Ty->isRecordType()) {
    // Handle an undefined struct
    clang::RecordDecl *Record = Ty->getAsRecordDecl();

    // This could either be a forward reference, or a struct defined inline. If
    // it is a forward reference, create it as an incomplete struct for now. If
    // it is defined, then create a proper type for it.
    if (needComplete && !Ty->isIncompleteType()) {
      VisitRecordDecl(Record);
      return TypeMap[Ty];
    }

    std::string TypeName = Record->getName().str();
    MIRStructType Struct(kTypeStructIncomplete);
    MIRType *MTy = GlobalTables::GetTypeTable().GetOrCreateMIRTypeNode(Struct);
    GStrIdx StrIdx =
        GlobalTables::GetStrTable().GetOrCreateStrIdxFromName(TypeName);
    MTy->SetNameStrIdx(StrIdx);
    Module->GetTypeNameTab()->SetGStrIdxToTyIdx(StrIdx, MTy->GetTypeIndex());
    Module->PushbackTypeDefOrder(StrIdx);
    TypeMap.insert({Ty, MTy->GetTypeIndex()});

    return MTy->GetTypeIndex();
  } else if (Ty->isEnumeralType()) {
    const clang::EnumType *EnumTy = llvm::dyn_cast<clang::EnumType>(Ty);
    clang::QualType QT = EnumTy->getDecl()->getIntegerType();
    TyIdx TI = type2MplIdx(QT);
    TypeMap.insert({Ty, TI});
    return TI;
  } else if (Ty->isVectorType()) {
    const clang::VectorType *VecTy = llvm::dyn_cast<clang::VectorType>(Ty);
    MIRType *ElemTy = type2Mpl(VecTy->getElementType());
    unsigned NumElements = VecTy->getNumElements();

    TyIdx TI(0);
    switch (ElemTy->GetPrimType()) {
    case PTY_i64:
      if (NumElements == 1) {
        TI = GlobalTables::GetTypeTable().GetPrimType(PTY_i64)->GetTypeIndex();
      } else if (NumElements == 2) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v2i64)->GetTypeIndex();
      } else {
        Ty->dump();
        ASSERT(false, "Unsupported vector type");
      }
      break;
    case PTY_i32:
      if (NumElements == 1) {
        TI = GlobalTables::GetTypeTable().GetPrimType(PTY_i64)->GetTypeIndex();
      } else if (NumElements == 2) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v2i32)->GetTypeIndex();
      } else if (NumElements == 4) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v4i32)->GetTypeIndex();
      } else if (NumElements == 8) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v8i16)->GetTypeIndex();
      } else if (NumElements == 16) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v16i8)->GetTypeIndex();
      } else {
        Ty->dump();
        ASSERT(false, "Unsupported vector type");
      }
      break;
    case PTY_i16:
      if (NumElements == 4) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v4i16)->GetTypeIndex();
      } else if (NumElements == 8) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v8i16)->GetTypeIndex();
      } else {
        Ty->dump();
        ASSERT(false, "Unsupported vector type");
      }
      break;
    case PTY_i8:
      if (NumElements == 8) {
        TI = GlobalTables::GetTypeTable().GetPrimType(PTY_v8i8)->GetTypeIndex();
      } else if (NumElements == 16) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v16i8)->GetTypeIndex();
      } else {
        Ty->dump();
        ASSERT(false, "Unsupported vector type");
      }
      break;
    case PTY_u64:
      if (NumElements == 1) {
        TI = GlobalTables::GetTypeTable().GetPrimType(PTY_u64)->GetTypeIndex();
      } else if (NumElements == 2) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v2u64)->GetTypeIndex();
      } else {
        Ty->dump();
        ASSERT(false, "Unsupported vector type");
      }
      break;
    case PTY_u32:
      if (NumElements == 2) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v2u32)->GetTypeIndex();
      } else if (NumElements == 4) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v4u32)->GetTypeIndex();
      } else {
        Ty->dump();
        ASSERT(false, "Unsupported vector type");
      }
      break;
    case PTY_u16:
      if (NumElements == 4) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v4u16)->GetTypeIndex();
      } else if (NumElements == 8) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v8u16)->GetTypeIndex();
      } else {
        Ty->dump();
        ASSERT(false, "Unsupported vector type");
      }
      break;
    case PTY_u8:
      if (NumElements == 8) {
        TI = GlobalTables::GetTypeTable().GetPrimType(PTY_v8u8)->GetTypeIndex();
      } else if (NumElements == 16) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v16u8)->GetTypeIndex();
      } else {
        Ty->dump();
        ASSERT(false, "Unsupported vector type");
      }
      break;
    case PTY_f64:
      if (NumElements == 1) {
        TI = GlobalTables::GetTypeTable().GetPrimType(PTY_f64)->GetTypeIndex();
      } else if (NumElements == 2) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v2f64)->GetTypeIndex();
      } else {
        Ty->dump();
        ASSERT(false, "Unsupported vector type");
      }
      break;
    case PTY_f32:
      if (NumElements == 2) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v2f32)->GetTypeIndex();
      } else if (NumElements == 4) {
        TI =
            GlobalTables::GetTypeTable().GetPrimType(PTY_v4f32)->GetTypeIndex();
      } else {
        Ty->dump();
        ASSERT(false, "Unsupported vector type");
      }
      break;
    default:
      Ty->dump();
      ASSERT(false, "Unsupported vector type");
      break;
    }

    TypeMap.insert({Ty, TI});
    return TI;
  }

  Ty->dump();
  ASSERT(false, "Failed to convert type");

  return TyIdx(0);
}

MIRType *Clang2MapleVisitor::type2Mpl(clang::QualType QT) {
  return GlobalTables::GetTypeTable().GetTypeFromTyIdx(type2MplIdx(QT));
}

int Clang2MapleVisitor::fileID2Mpl(clang::FileID FID) {
  auto it = FileMap.find(FID.getHashValue());
  if (it != FileMap.end()) {
    return it->second;
  }

  // If this FileID is not in the map, create it
  const clang::FileEntry *File =
      Context->getSourceManager().getFileEntryForID(FID);
  std::string FileName;
  if (File) {
    FileName = File->getName().str().c_str();
  } else {
    FileName = "invalid_file";
  }
  GStrIdx StrIdx =
      GlobalTables::GetStrTable().GetOrCreateStrIdxFromName(FileName);
  int Idx = FileIdx++;
  Module->PushbackFileInfo(MIRInfoPair(StrIdx, Idx));
  FileMap.insert({FID.getHashValue(), Idx});
  return Idx;
}

MIRSymbol *Clang2MapleVisitor::decl2Mpl(const clang::Decl *D) {
  auto it = DeclMap.find(D);
  if (it != DeclMap.end()) {
    return it->second;
  }

  // Check for use of an implicit decl. This shows up for builtins like
  // `abort`.
  if (D->isImplicit()) {
    return Visit(D);
  }

  D->dump();
  ASSERT(false, "Failed to find decl");
  return nullptr;
}

SrcPosition Clang2MapleVisitor::sourceLocation2Mpl(clang::SourceLocation Loc) {
  SrcPosition SrcPos;
  if (!Loc.isValid()) {
    return SrcPos;
  }

  clang::SourceManager &SM = Context->getSourceManager();

  if (Loc.isFileID()) {
    clang::PresumedLoc PLoc = SM.getPresumedLoc(Loc);

    if (PLoc.isInvalid()) {
      return SrcPos;
    }

    SrcPos.SetFileNum(fileID2Mpl(PLoc.getFileID()));
    SrcPos.SetLineNum(PLoc.getLine());
    SrcPos.SetColumn(PLoc.getColumn());
    return SrcPos;
  }

  return sourceLocation2Mpl(SM.getExpansionLoc(Loc));
}

void Clang2MapleVisitor::addStmtToBlock(BlockNode *Block, StmtNode *SNode) {
  // Merge block into this one
  if (SNode->GetOpCode() == OP_block) {
    BlockNode *InnerBlock = static_cast<BlockNode *>(SNode);
    Block->AppendStatementsFromBlock(*InnerBlock);
  } else {
    Block->AddStatement(SNode);
  }
}

void Clang2MapleVisitor::addToBlock(BlockNode *Block, Result &Res) {
  for (Result::iterator It = Res.beginStmtsBefore(); It != Res.endStmtsBefore();
       ++It) {
    Block->AddStatement(*It);
  }

  if (!Res.mayDrop()) {
    if (StmtNode *SNode = getNodeAsStmt(Res)) {
      addStmtToBlock(Block, SNode);
    }
  }

  for (Result::iterator It = Res.beginStmtsAfter(); It != Res.endStmtsAfter();
       ++It) {
    Block->AddStatement(*It);
  }
}

BaseNode *Clang2MapleVisitor::exprToCond(BaseNode *Cond) {
  if (kOpcodeInfo.IsCompare(Cond->op))
    return Cond;
  if (Cond->op == OP_cand || Cond->op == OP_cior)
    return Cond;
  MIRType *IntTy =
      GlobalTables::GetTypeTable().GetTypeFromTyIdx(TyIdx(PTY_i32));
  MIRType *OpndTy =
      GlobalTables::GetTypeTable().GetTypeFromTyIdx(TyIdx(Cond->GetPrimType()));
  switch (Cond->GetPrimType()) {
  case PTY_i8:
  case PTY_i16:
  case PTY_i32:
  case PTY_i64:
  case PTY_u8:
  case PTY_u16:
  case PTY_u32:
  case PTY_u64:
  case PTY_a32:
  case PTY_a64: {
    ConstvalNode *Zero = Builder->CreateIntConst(0, Cond->GetPrimType());
    return Builder->CreateExprCompare(OP_ne, *IntTy, *OpndTy, Cond, Zero);
  }
  case PTY_f32: {
    ConstvalNode *Zero = Builder->CreateFloatConst(0.0f);
    return Builder->CreateExprCompare(OP_ne, *IntTy, *OpndTy, Cond, Zero);
  }
  case PTY_f64: {
    ConstvalNode *Zero = Builder->CreateDoubleConst(0.0);
    return Builder->CreateExprCompare(OP_ne, *IntTy, *OpndTy, Cond, Zero);
  }
  case PTY_ptr: {
    ConstvalNode *Zero = Builder->CreateIntConst(0, PointerPrimTy);
    return Builder->CreateExprCompare(OP_ne, *IntTy, *OpndTy, Cond, Zero);
  }
  default:
    LogInfo::MapleLogger() << "Warning: Unhandled type in exprToCond\n";
#ifdef DEBUG
    Cond->Dump();
#endif
    CHECK_FATAL(false, "Failing due to unsupported feature");
    return Cond;
  }
}

static Opcode notCondOperator(Opcode op) {
  switch (op) {
  case OP_eq:
    return OP_ne;
  case OP_ge:
    return OP_lt;
  case OP_gt:
    return OP_le;
  case OP_le:
    return OP_gt;
  case OP_lt:
    return OP_ge;
  case OP_ne:
    return OP_eq;
  default:
    LogInfo::MapleLogger() << "Error: Unhandled operator in notCondOperator: "
                           << op << "\n";
    CHECK_FATAL(false, "Failing due to unsupported feature");
    return OP_undef;
  }
}

BaseNode *Clang2MapleVisitor::exprToNotCond(BaseNode *Cond) {
  if (kOpcodeInfo.IsCompare(Cond->op)) {
    CompareNode *Compare = static_cast<CompareNode *>(Cond);
    Compare->SetOpCode(notCondOperator(Compare->GetOpCode()));
    return Cond;
  }
  MIRType *IntTy =
      GlobalTables::GetTypeTable().GetTypeFromTyIdx(TyIdx(PTY_i32));
  MIRType *OpndTy =
      GlobalTables::GetTypeTable().GetTypeFromTyIdx(TyIdx(Cond->GetPrimType()));
  switch (Cond->GetPrimType()) {
  case PTY_i8:
  case PTY_i16:
  case PTY_i32:
  case PTY_i64:
  case PTY_u8:
  case PTY_u16:
  case PTY_u32:
  case PTY_u64:
  case PTY_a32:
  case PTY_a64: {
    ConstvalNode *Zero = Builder->CreateIntConst(0, Cond->GetPrimType());
    return Builder->CreateExprCompare(OP_eq, *IntTy, *OpndTy, Cond, Zero);
  }
  case PTY_f32: {
    ConstvalNode *Zero = Builder->CreateFloatConst(0.0f);
    return Builder->CreateExprCompare(OP_eq, *IntTy, *OpndTy, Cond, Zero);
  }
  case PTY_f64: {
    ConstvalNode *Zero = Builder->CreateDoubleConst(0.0);
    return Builder->CreateExprCompare(OP_eq, *IntTy, *OpndTy, Cond, Zero);
  }
  case PTY_ptr: {
    ConstvalNode *Zero = Builder->CreateIntConst(0, PointerPrimTy);
    return Builder->CreateExprCompare(OP_eq, *IntTy, *OpndTy, Cond, Zero);
  }
  default:
    LogInfo::MapleLogger() << "Error: Unhandled type in exprToCond\n";
#ifdef DEBUG
    Cond->Dump();
#endif
    CHECK_FATAL(false, "Failing due to unsupported feature");
    return Cond;
  }
}

FieldID Clang2MapleVisitor::recordFieldIDs(const clang::RecordDecl *Record,
                                           const clang::RecordDecl *Ancestor,
                                           FieldID Base) {
  FieldID CurrentField = Base;
  for (const auto *I : Record->fields()) {
    CurrentField++;
    FieldMap[Ancestor][Base][I] = CurrentField;
    const clang::Type *FieldType = I->getType().getTypePtr();
    // The fields of the struct type of an embedded array are assigned field
    // IDs, so get down to the element of the array.
    while (const clang::ArrayType *ArrTy = llvm::dyn_cast<clang::ArrayType>(
               FieldType->getUnqualifiedDesugaredType())) {
      FieldType = ArrTy->getElementType().getTypePtr();
    }
    if (FieldType->isRecordType()) {
      CurrentField +=
          recordFieldIDs(FieldType->getAsRecordDecl(), Ancestor, CurrentField);
    }
  }

  return CurrentField - Base; // Number of fields
}

BaseNode *Clang2MapleVisitor::getNodeAsLVal(Result &Res) {
  if (Res.isDeref()) {
    // Do not dereference arrays
    if (Res.getValueTy()->GetKind() == kTypeArray && Res.getField() == 0) {
      return Res.getAddr();
    }

    return Builder->CreateExprIaddrof(*Res.getValueTy(), *Res.getAddrTy(),
                                      Res.getField(), Res.getAddr());
  }
  return getNodeAsRVal(Res);
}

BaseNode *Clang2MapleVisitor::getNodeAsRVal(Result &Res) {
  BaseNode *N = Res.getNode();
  if (N) {
    if (Res.isDeref()) {
      // Do not dereference arrays
      if (Res.getValueTy()->GetKind() == kTypeArray && Res.getField() == 0) {
        return Res.getAddr();
      }
      // Do not dereference functions
      if (Res.getValueTy()->GetKind() == kTypeFunction) {
        return Res.getAddr();
      }

      return Builder->CreateExprIread(*Res.getValueTy(), *Res.getAddrTy(),
                                      Res.getField(), Res.getAddr());
    }

    // If a call is used as an rvalue, we need to create a temporary to assign
    // the return value to, then read from that for the rvalue.
    Opcode Op = N->GetOpCode();
    if (kOpcodeInfo.IsCall(Op)) {
      MIRType *RetValTy = Res.getValueTy();

      // If there is a field access, then we need to create a temp for the
      // struct, then get the field.
      if (Res.getField()) {
        RetValTy = type2Mpl(Context->getRecordType(Res.getBaseRecordDecl()));
      }

      // Generate a temp for result of the function, then read that as the
      // rval
      MIRSymbol *Sym = Builder->GetOrCreateLocalDecl(
          "_result" + std::to_string(Res.getUniqueID()), *RetValTy);
      Sym->SetAttrs(Res.getValueTyAttrs());
      StmtNode *TmpAssign = nullptr;
      if (Op == OP_call) {
        CallNode *Call = static_cast<CallNode *>(N);
        // If the return type is a structure too big to fit in registers, then
        // pass the address of the return value as an implicit first parameter
        // to the function.
        MapleVector<BaseNode *> &Args = Call->GetNopnd();
        if (RetValTy->GetSize() > 16) {
          BaseNode *AddrOf = Builder->CreateExprAddrof(0, *Sym);
          AddrOf->SetPrimType(PointerPrimTy);
          Args.insert(Args.begin(), AddrOf);
          Call->SetNumOpnds(Args.size());
          TmpAssign = Call;
        } else {
          TmpAssign = Builder->CreateStmtCallAssigned(
              Call->GetPUIdx(), Call->GetNopnd(), Sym, OP_callassigned);
        }
      } else if (Op == OP_icall) {
        IcallNode *Call = static_cast<IcallNode *>(N);
        // If the return type is a structure too big to fit in registers, then
        // pass the address of the return value as an implicit first parameter
        // to the function.
        MapleVector<BaseNode *> &Args = Call->GetNopnd();
        if (RetValTy->GetSize() > 16) {
          BaseNode *AddrOf = Builder->CreateExprAddrof(0, *Sym);
          AddrOf->SetPrimType(PointerPrimTy);
          Args.insert(Args.begin() + 1, AddrOf);
          Call->SetNumOpnds(Args.size());
          TmpAssign = Call;
        } else {
          TmpAssign = Builder->CreateStmtIcallAssigned(Call->GetNopnd(), *Sym);
        }
      } else if (Op == OP_intrinsiccall) {
        IntrinsiccallNode *Call = static_cast<IntrinsiccallNode *>(N);
        // If the return type is a structure too big to fit in registers, then
        // pass the address of the return value as an implicit first parameter
        // to the function.
        MapleVector<BaseNode *> &Args = Call->GetNopnd();
        if (RetValTy->GetSize() > 16) {
          BaseNode *AddrOf = Builder->CreateExprAddrof(0, *Sym);
          AddrOf->SetPrimType(PointerPrimTy);
          Args.insert(Args.begin(), AddrOf);
          Call->SetNumOpnds(Args.size());
          TmpAssign = Call;
        } else {
          TmpAssign = Builder->CreateStmtIntrinsicCallAssigned(
              Call->GetIntrinsic(), Call->GetNopnd(), Sym);
        }
      } else {
        LogInfo::MapleLogger()
            << "Error: Unhandled call node in getNodeAsRVal:\n";
#ifdef DEBUG
        N->Dump();
#endif
        CHECK_FATAL(false, "Failing due to unsupported feature");
      }
      TmpAssign->SetSrcPos(sourceLocation2Mpl(Res.getLoc()));
      Res.appendStmtBefore(TmpAssign);

      // If a struct is returned, the later passes need an assignment, other
      // than the callassigned, to properly retrieve the struct.
      if (RetValTy->IsStructType()) {
        MIRSymbol *StructSym = Builder->GetOrCreateLocalDecl(
            "_resultStruct" + std::to_string(Res.getUniqueID()), *RetValTy);
        TmpAssign = Builder->CreateStmtDassign(
            *StructSym, 0, Builder->CreateExprDread(*RetValTy, 0, *Sym));
        TmpAssign->SetSrcPos(sourceLocation2Mpl(Res.getLoc()));
        Res.appendStmtBefore(TmpAssign);
        Sym = StructSym;
      }

      return Builder->CreateExprDread(*Res.getValueTy(), Res.getField(), *Sym);
    } else if (isAssign(Op)) {
      // If the RHS of an assignment is another assignment, then we need to
      // emit that assignment statement first, then use the LHS of that
      // assignment as the operand.
      StmtNode *SNode = static_cast<StmtNode *>(N);
      SNode->SetSrcPos(sourceLocation2Mpl(Res.getLoc()));
      Res.appendStmtBefore(SNode);
      if (Op == OP_dassign) {
        DassignNode *Assign = static_cast<DassignNode *>(N);
        return Builder->GetCurrentFuncCodeMp()->New<AddrofNode>(
            OP_dread, Assign->GetRHS()->GetPrimType(), Assign->GetStIdx(),
            Assign->GetFieldID());
      } else if (Op == OP_iassign) {
        IassignNode *Assign = static_cast<IassignNode *>(N);
        MIRPtrType *PtrTy = static_cast<MIRPtrType *>(
            GlobalTables::GetTypeTable().GetTypeFromTyIdx(Assign->GetTyIdx()));
        BaseNode *Addr = Assign->Opnd(0);
        MIRType *Ty = PtrTy->GetPointedType();
        if (Assign->GetFieldID()) {
          MIRStructType *StructTy = static_cast<MIRStructType *>(Ty);
          Ty = StructTy->GetFieldType(Assign->GetFieldID());
        }
        return Builder->CreateExprIread(*Ty, *PtrTy, Assign->GetFieldID(),
                                        Addr);
      } else if (kOpcodeInfo.IsCallAssigned(Op)) {
        if (Op == OP_callassigned) {
          CallNode *Assign = static_cast<CallNode *>(N);
          CallReturnPair Ret = Assign->GetReturnPair(0);
          return Builder->GetCurrentFuncCodeMp()->New<AddrofNode>(
              OP_dread, Assign->GetCallReturnType()->GetPrimType(), Ret.first,
              Ret.second.GetFieldID());
        } else if (Op == OP_icallassigned) {
          IcallNode *Assign = static_cast<IcallNode *>(N);
          CallReturnPair Ret = Assign->GetReturnVec()[0];
          return Builder->GetCurrentFuncCodeMp()->New<AddrofNode>(
              OP_dread, Assign->GetCallReturnType()->GetPrimType(), Ret.first,
              Ret.second.GetFieldID());
        } else {
          LogInfo::MapleLogger()
              << "Unhandled call-assign node in getNodeAsRVal:\n";
#ifdef DEBUG
          N->Dump();
#endif
          CHECK_FATAL(false, "Failing due to unsupported feature");
        }
      }
    } else if (Op == OP_iread) {
      FieldID origFieldID = static_cast<IreadNode*>(N)->GetFieldID();
      static_cast<IreadNode*>(N)->SetFieldID(origFieldID + Res.getField());
    }

    return N;
  } else if (Res.isSym()) {
    MIRSymbol *Sym = Res.getSym();
    if (Sym->GetSKind() == kStFunc) {
      MIRFunction *Func = Sym->GetFunction();
      BaseNode *AddrOf = Builder->CreateExprAddroffunc(Func->GetPuidx());
      AddrOf->SetPrimType(PointerPrimTy);
      Func->GetFuncSymbol()->SetAppearsInCode(true);
      return AddrOf;
    }
    return Builder->CreateExprDread(*Res.getValueTy(), Res.getField(), *Sym);
  }

  return nullptr;
}

StmtNode *Clang2MapleVisitor::getNodeAsStmt(Result &Res) {
  BaseNode *N = Res.getNode();
  if (Res.isDeref()) {
    // Do not dereference arrays
    if (Res.getValueTy()->GetKind() == kTypeArray) {
      N = Res.getAddr();
    } else {
      N = Builder->CreateExprIread(*Res.getValueTy(), *Res.getAddrTy(),
                                   Res.getField(), Res.getAddr());
    }
  } else if (Res.isSym()) {
    MIRSymbol *Sym = Res.getSym();
    if (Sym->GetSKind() == kStFunc) {
      MIRFunction *Func = Sym->GetFunction();
      N = Builder->CreateExprAddroffunc(Func->GetPuidx());
      N->SetPrimType(PointerPrimTy);
      Func->GetFuncSymbol()->SetAppearsInCode(true);
    } else {
      N = Builder->CreateExprDread(*Res.getValueTy(), Res.getField(), *Sym);
    }
  }

  StmtNode *SNode = nullptr;
  if (N) {
    Opcode Op = N->GetOpCode();
    // If this is a call with a return type which must be passed by reference,
    // generate a temp and pass its address as an implicit first parameter.
    if (kOpcodeInfo.IsCall(Op)) {
      MIRType *RetValTy = Res.getValueTy();

      // If there is a field access, then we need to create a temp for the
      // struct, then get the field.
      if (Res.getField()) {
        RetValTy = type2Mpl(Context->getRecordType(Res.getBaseRecordDecl()));
      }

      if (RetValTy->GetSize() > 16) {
        // Generate a temp for result of the function
        MIRSymbol *Sym = Builder->GetOrCreateLocalDecl(
            "_result" + std::to_string(Res.getUniqueID()), *RetValTy);
        Sym->SetAttrs(Res.getValueTyAttrs());
        StmtNode *TmpAssign = nullptr;
        if (Op == OP_call) {
          CallNode *Call = static_cast<CallNode *>(N);
          MapleVector<BaseNode *> &Args = Call->GetNopnd();
          BaseNode *AddrOf = Builder->CreateExprAddrof(0, *Sym);
          AddrOf->SetPrimType(PointerPrimTy);
          Args.insert(Args.begin(), AddrOf);
          Call->SetNumOpnds(Args.size());
        } else if (Op == OP_icall) {
          IcallNode *Call = static_cast<IcallNode *>(N);
          MapleVector<BaseNode *> &Args = Call->GetNopnd();
          BaseNode *AddrOf = Builder->CreateExprAddrof(0, *Sym);
          AddrOf->SetPrimType(PointerPrimTy);
          Args.insert(Args.begin() + 1, AddrOf);
          Call->SetNumOpnds(Args.size());
        } else if (Op == OP_intrinsiccall) {
          IntrinsiccallNode *Call = static_cast<IntrinsiccallNode *>(N);
          MapleVector<BaseNode *> &Args = Call->GetNopnd();
          BaseNode *AddrOf = Builder->CreateExprAddrof(0, *Sym);
          AddrOf->SetPrimType(PointerPrimTy);
          Args.insert(Args.begin(), AddrOf);
          Call->SetNumOpnds(Args.size());
        } else {
          LogInfo::MapleLogger()
              << "Error: Unhandled call node in getNodeAsRVal:\n";
#ifdef DEBUG
          N->Dump();
#endif
          CHECK_FATAL(false, "Failing due to unsupported feature");
        }
      }

      SNode = static_cast<StmtNode *>(N);
    } else if (kOpcodeInfo.IsStmt(Op)) {
      SNode = static_cast<StmtNode *>(N);
    } else {
      // If the node is an expression, not a statement, wrap it in an eval
      SNode = Builder->CreateStmtUnary(OP_eval, N);
    }
  }

  SNode->SetSrcPos(sourceLocation2Mpl(Res.getLoc()));
  return SNode;
}

BaseNode *Clang2MapleVisitor::getNodeAsAddrOf(Result &Res) {
  BaseNode *AddrOf = nullptr;
  if (Res.isSym()) {
    MIRSymbol *Sym = Res.getSym();
    if (Sym->GetSKind() == kStFunc) {
      AddrOf = Builder->CreateExprAddroffunc(Sym->GetFunction()->GetPuidx());
    } else {
      AddrOf = Builder->CreateExprAddrof(Res.getField(), *Sym);
    }
    AddrOf->SetPrimType(PointerPrimTy);
    return AddrOf;
  } else {
    BaseNode *N = getNodeAsRVal(Res);
    if (N->GetOpCode() == OP_array) {
      AddrOf = N;
      return AddrOf;
    } else if (N->GetOpCode() == OP_iread) {
      IreadNode *IRead = static_cast<IreadNode *>(N);
      if (N->Opnd(0)->GetOpCode() == OP_array && !IRead->GetFieldID()) {
        AddrOf = N->Opnd(0);
      } else {
        AddrOf = Builder->CreateExprIaddrof(Res.getValueTy()->GetPrimType(),
                                            IRead->GetTyIdx(),
                                            IRead->GetFieldID(), N->Opnd(0));
        AddrOf->SetPrimType(PointerPrimTy);
      }
      return AddrOf;
    }
    LogInfo::MapleLogger()
        << "Error: Unhandled expression in getNodeAsAddrOf\n";
#ifdef DEBUG
    N->Dump();
#endif
    CHECK_FATAL(false, "Failing due to unsupported feature");
  }
  return AddrOf;
}

void Clang2MapleVisitor::appendResultBefore(Result &To, Result R) {
  for (Result::iterator It = R.beginStmtsBefore(); It != R.endStmtsBefore();
       ++It) {
    To.appendStmtBefore(*It);
  }

  if (!R.mayDrop()) {
    if (StmtNode *SNode = getNodeAsStmt(R)) {
      To.appendStmtBefore(SNode);
    }
  }

  for (Result::iterator It = R.beginStmtsAfter(); It != R.endStmtsAfter();
       ++It) {
    To.appendStmtBefore(*It);
  }
}

bool Clang2MapleVisitor::evaluateNodeAsInt(int64_t &Result, const BaseNode *N) {
  if (N->GetOpCode() == OP_constval) {
    const ConstvalNode *ConstNode = static_cast<const ConstvalNode *>(N);
    const MIRConst *Const = ConstNode->GetConstVal();
    if (Const->GetKind() == kConstInt) {
      const MIRIntConst *IntConst = static_cast<const MIRIntConst *>(Const);
      Result = IntConst->GetValue().GetSXTValue();
      return true;
    }
  }
  return false;
}

MIRConst *Clang2MapleVisitor::evaluateExprAsConst(const clang::Expr *E,
                                                  MIRType *Ty) {
  clang::Expr::EvalResult InitConstResult;
  if (E->EvaluateAsConstantExpr(InitConstResult, *Context)) {
    clang::APValue InitConstVal = InitConstResult.Val;
    if (InitConstVal.isInt()) {
      llvm::APSInt IntVal = InitConstVal.getInt();
      MIRConst *IntConst = GlobalTables::GetIntConstTable().GetOrCreateIntConst(
          IntVal.getExtValue(), *Ty);
      return IntConst;
    } else if (InitConstVal.isFloat()) {
      MIRConst *FloatConst = nullptr;
      llvm::APFloat FloatVal = InitConstVal.getFloat();
      if (&FloatVal.getSemantics() == &llvm::APFloat::IEEEsingle()) {
        FloatConst = Builder->GetCurrentFuncDataMp()->New<MIRFloatConst>(
            FloatVal.convertToFloat(),
            *GlobalTables::GetTypeTable().GetPrimType(PTY_f32));
      } else if (&FloatVal.getSemantics() == &llvm::APFloat::IEEEdouble()) {
        FloatConst = Builder->GetCurrentFuncDataMp()->New<MIRDoubleConst>(
            FloatVal.convertToDouble(),
            *GlobalTables::GetTypeTable().GetPrimType(PTY_f64));
      } else if (&FloatVal.getSemantics() == &llvm::APFloat::IEEEquad()) {
        bool LosesInfo;
        FloatVal.convert(llvm::APFloat::IEEEdouble(),
                         llvm::APFloatBase::roundingMode::NearestTiesToAway,
                         &LosesInfo);
        FloatConst = Builder->GetCurrentFuncDataMp()->New<MIRDoubleConst>(
            FloatVal.convertToDouble(),
            *GlobalTables::GetTypeTable().GetPrimType(PTY_f64));
      } else {
        LogInfo::MapleLogger()
            << "Unexpected type of floating literal: " << Ty->GetPrimType()
            << "\n";
      }
      return FloatConst;
    }
  }
  return nullptr;
}

MIRConst *Clang2MapleVisitor::createZero(MIRType *Ty) {
  PrimType PTy = Ty->GetPrimType();
  if (PrimitiveType(PTy).IsAddress()) {
    return GlobalTables::GetIntConstTable().GetOrCreateIntConst(0,
                                                                *IntPointerTy);
  } else if (PrimitiveType(PTy).IsInteger()) {
    return GlobalTables::GetIntConstTable().GetOrCreateIntConst(0, *Ty);
  } else if (PTy == PTY_f32) {
    return Builder->GetCurrentFuncDataMp()->New<MIRFloatConst>(0.0f, *Ty);
  } else if (PTy == PTY_f64) {
    return Builder->GetCurrentFuncDataMp()->New<MIRDoubleConst>(0.0, *Ty);
  } else if (Ty->IsStructType()) {
    MIRStructType *StructTy = static_cast<MIRStructType *>(Ty);
    MIRAggConst *Agg =
        Builder->GetCurrentFuncDataMp()->New<MIRAggConst>(*Module, *Ty);
    for (size_t Field = 0; Field < StructTy->GetFieldsSize(); Field++) {
      MIRType *FieldTy = StructTy->GetElemType(Field);
      MIRConst *Zero = createZero(FieldTy);
      Agg->AddItem(Zero, Field + 1);
    }
    return Agg;
  } else if (Ty->GetKind() == kTypeArray) {
    MIRArrayType *ArrTy = static_cast<MIRArrayType *>(Ty);
    if (PrimitiveType(ArrTy->GetElemType()->GetPrimType()).IsInteger()) {
      return GlobalTables::GetIntConstTable().GetOrCreateIntConst(
          0, *ArrTy->GetElemType());
    } else {
      MIRAggConst *Agg =
          Builder->GetCurrentFuncDataMp()->New<MIRAggConst>(*Module, *Ty);
      MIRConst *Zero = createZero(ArrTy->GetElemType());
      for (size_t i = 0; i < ArrTy->GetSizeArrayItem(0); i++) {
        Agg->PushBack(Zero);
      }
      return Agg;
    }
  }

  LogInfo::MapleLogger() << "Error: createZero does not yet handle PrimType "
                         << PTy << "\n";
  return nullptr;
}

BaseNode *Clang2MapleVisitor::createVectorZero(MIRType *Ty) {
  PrimType PTy = Ty->GetPrimType();
  MapleVector<BaseNode *> Args(
      Builder->GetCurrentFuncCodeMpAllocator()->Adapter());
  MIRConst *ScalarZero = createZero(
      GlobalTables::GetTypeTable().GetPrimType(getVectorElementPrimType(PTy)));
  Args.push_back(Builder->CreateConstval(ScalarZero));
  switch (PTy) {
  case PTY_v2i64:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v2i64,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v4i32:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v4i32,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v8i16:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v8i16,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v16i8:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v16i8,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v2u64:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v2u64,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v4u32:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v4u32,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v8u16:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v8u16,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v16u8:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v16u8,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v2f64:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v2f64,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v4f32:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v4f32,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v2i32:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v2i32,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v4i16:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v4i16,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v8i8:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v8i8,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v2u32:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v2u32,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v4u16:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v4u16,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v8u8:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v8u8,
                                          OP_intrinsicop, *Ty, Args);
  case PTY_v2f32:
    return Builder->CreateExprIntrinsicop(INTRN_vector_from_scalar_v2f32,
                                          OP_intrinsicop, *Ty, Args);
  default:
    LogInfo::MapleLogger()
        << "Error: Unhandled vector type in createVectorZero\n";
#ifdef DEBUG
    Ty->Dump(0);
#endif
    CHECK_FATAL(false, "Failing due to unsupported feature");
  }

  return nullptr;
}

Result
Clang2MapleVisitor::buildExprToComputeSizeFromVLA(const clang::Expr *E,
                                                  const clang::QualType &QT) {
  Result Res(E->getBeginLoc());
  const clang::Type *Ty = QT.getCanonicalType().getTypePtrOrNull();
  MIRType *SizeTy = type2Mpl(Context->getSizeType());
  MIR_ASSERT(!Ty->isArrayType() ||
             (Ty->isConstantArrayType() || Ty->isVariableArrayType()));
  if (Ty->isArrayType()) {
    BaseNode *LHS = nullptr, *RHS = nullptr;
    Result ResLHS = buildExprToComputeSizeFromVLA(
        E, llvm::dyn_cast<clang::ArrayType>(Ty)->getElementType());
    Res.appendStmts(ResLHS);
    LHS = getNodeAsRVal(ResLHS);
    if (Ty->isVariableArrayType()) {
      clang::Expr *Size =
          llvm::dyn_cast<clang::VariableArrayType>(Ty)->getSizeExpr();
      Result ResRHS = Visit(Size);
      Res.appendStmts(ResRHS);
      RHS = getNodeAsRVal(ResRHS);
      MIR_ASSERT(Size->getType()->isIntegerType());
      if (!Size->getType()->hasUnsignedIntegerRepresentation()) {
        RHS = Builder->CreateExprTypeCvt(OP_cvt, SizeTy->GetPrimType(),
                                         RHS->GetPrimType(), *RHS);
      }
    } else {
      llvm::APInt Size =
          llvm::dyn_cast<clang::ConstantArrayType>(Ty)->getSize();
      RHS = Builder->CreateIntConst(Size.getSExtValue(),
                                    IntPointerTy->GetPrimType());
    }
    Res.setNode(Builder->CreateExprBinary(OP_mul, *SizeTy, LHS, RHS),
                E->getID(*Context), SizeTy);
  } else {
    MIRType *MTy = type2Mpl(QT);
    Res.setNode(Builder->CreateIntConst(MTy->GetSize(), SizeTy->GetPrimType()),
                E->getID(*Context), SizeTy);
  }

  return Res;
}

BaseNode *Clang2MapleVisitor::constToNode(MIRConst *Const) {
  // Maple cannot handle an addrof node with an offset. Expand it into an
  // addition.
  if (Const->GetKind() == kConstAddrof) {
    MIRAddrofConst *AddrOf = static_cast<MIRAddrofConst *>(Const);
    if (AddrOf->GetOffset()) {
      BaseNode *Offset =
          Builder->CreateIntConst(AddrOf->GetOffset(), PointerPrimTy);
      BaseNode *Addr = Builder->CreateExprAddrof(AddrOf->GetFieldID(),
                                                 AddrOf->GetSymbolIndex());
      Addr->SetPrimType(PointerPrimTy);
      return Builder->CreateExprBinary(OP_add, Const->GetType(), Addr, Offset);
    } else {
      BaseNode *Node = Builder->CreateExprAddrof(AddrOf->GetFieldID(),
                                                 AddrOf->GetSymbolIndex());
      Node->SetPrimType(PointerPrimTy);
      return Node;
    }
  } else if (Const->GetKind() == kConstAddrofFunc) {
    MIRAddroffuncConst *AOFConst = static_cast<MIRAddroffuncConst *>(Const);
    BaseNode *AddrOf = Builder->CreateExprAddroffunc(AOFConst->GetValue());
    AddrOf->SetPrimType(PointerPrimTy);
    return AddrOf;
  } else if (Const->GetKind() == kConstLblConst) {
    MIRLblConst *LabelConst = static_cast<MIRLblConst *>(Const);
    AddroflabelNode *AOL =
        Builder->GetCurrentFuncCodeMp()->New<AddroflabelNode>(
            LabelConst->GetValue());
    AOL->SetPrimType(PointerPrimTy);
    return AOL;
  } else if (Const->GetKind() == kConstStrConst) {
    MIRStrConst *StrConst = static_cast<MIRStrConst *>(Const);
    return Builder->GetCurrentFuncCodeMp()->New<ConststrNode>(
        PointerPrimTy, StrConst->GetValue());
  }

  return Builder->CreateConstval(Const);
}

unsigned Clang2MapleVisitor::assignStruct(Result &Res, MIRSymbol *BaseSym,
                                          BaseNode *BaseAddr,
                                          MIRStructType *StructTy,
                                          BaseNode *Src, FieldID Base) {
  MIRType *StructPtrTy = GlobalTables::GetTypeTable().GetOrCreatePointerType(
      *StructTy, PointerPrimTy);
  SrcPosition Pos = sourceLocation2Mpl(Res.getLoc());
  if (Src->op == OP_constval) { // constant initializers
    ConstvalNode *ConstNode = static_cast<ConstvalNode *>(Src);
    MIRConst *Const = ConstNode->GetConstVal();
    ASSERT(Const->GetKind() == kConstAggConst,
           "Expected agg constant in struct initializer\n");
    MIRAggConst *Agg = static_cast<MIRAggConst *>(Const);
    unsigned Size = Agg->GetConstVec().size();
    for (unsigned i = 0; i < Size; i++) {
      MIRConst *FieldConst = Agg->GetConstVecItem(i);
      FieldID Field = Base + Agg->GetFieldIdItem(i);
      MIRType *FieldTy = StructTy->GetFieldType(Field);
      if (FieldTy->GetKind() == kTypeScalar ||
          FieldTy->GetKind() == kTypePointer ||
          FieldTy->GetKind() == kTypeBitField) {
        BaseNode *FieldNode = constToNode(FieldConst);
        StmtNode *FieldAssign;
        if (BaseSym) {
          FieldAssign = Builder->CreateStmtDassign(*BaseSym, Field, FieldNode);
        } else {
          FieldAssign = Builder->CreateStmtIassign(
              *StructPtrTy, Field,
              BaseAddr->CloneTree(*Builder->GetCurrentFuncCodeMpAllocator()),
              FieldNode);
        }
        FieldAssign->SetSrcPos(Pos);
        Res.appendStmtBefore(FieldAssign);
      } else {
        ConstvalNode FieldNode(FieldConst);
        if (FieldTy->IsStructType()) {
          assignStruct(Res, BaseSym, BaseAddr, StructTy, &FieldNode, Field);
        } else if (FieldTy->GetKind() == kTypeArray) {
          BaseNode *ElemBaseAddr = getBase(BaseSym, BaseAddr, Base);
          MIRArrayType *ArrayTy = static_cast<MIRArrayType *>(FieldTy);
          if (ElemBaseAddr->GetOpCode() == OP_iread ||
              ElemBaseAddr->GetOpCode() == OP_iaddrof) {
            IreadNode *IRead = static_cast<IreadNode *>(ElemBaseAddr);
            IRead->SetFieldID(Field);
          } else if (ElemBaseAddr->GetOpCode() == OP_addrof ||
                     ElemBaseAddr->GetOpCode() == OP_dread) {
            AddrofNode *AddrOf = static_cast<AddrofNode *>(ElemBaseAddr);
            AddrOf->SetFieldID(Field);
          } else {
            ElemBaseAddr->Dump();
            ASSERT(false, "Unexpected base address in assignStruct");
          }
          assignArray(Res, ElemBaseAddr, ArrayTy, &FieldNode);
        } else {
          FieldConst->Dump(nullptr);
          LogInfo::MapleLogger() << "\n";
          FieldTy->Dump(0, false);
          ASSERT(false, "Error: field is not a scalar, struct, or array\n");
        }
      }
    }
    return Size + 1;
  } else if (Src->GetOpCode() ==
             OP_intrinsicopwithtype) { // non-constant initializers
    IntrinsicopNode *N = static_cast<IntrinsicopNode *>(Src);
    unsigned Size = N->NumOpnds();
    for (unsigned i = 0; i < Size; i++) {
      IreadNode *WrapperNode = static_cast<IreadNode *>(N->Opnd(i));
      BaseNode *FieldNode = WrapperNode->Opnd(0);
      FieldID Field = Base + WrapperNode->GetFieldID();
      if (FieldNode->GetPrimType() == PTY_agg &&
          (FieldNode->GetOpCode() == OP_intrinsicopwithtype ||
           FieldNode->GetOpCode() == OP_constval)) {
        MIRType *FieldTy;
        if (FieldNode->GetOpCode() == OP_intrinsicopwithtype) {
          IntrinsicopNode *NField = static_cast<IntrinsicopNode *>(FieldNode);
          FieldTy =
              GlobalTables::GetTypeTable().GetTypeFromTyIdx(NField->GetTyIdx());
        } else { // FieldNode->GetOpCode() == OP_constval
          ConstvalNode *ConstNode = static_cast<ConstvalNode *>(FieldNode);
          MIRConst *Const = ConstNode->GetConstVal();
          FieldTy = &Const->GetType();
        }

        if (FieldTy->GetKind() == kTypeArray) {
          BaseNode *ElemBaseAddr = getBase(BaseSym, BaseAddr, Base);
          MIRArrayType *ArrayTy = static_cast<MIRArrayType *>(FieldTy);
          if (ElemBaseAddr->GetOpCode() == OP_iread ||
              ElemBaseAddr->GetOpCode() == OP_iaddrof) {
            IreadNode *IRead = static_cast<IreadNode *>(ElemBaseAddr);
            IRead->SetFieldID(Field);
          } else if (ElemBaseAddr->GetOpCode() == OP_addrof ||
                     ElemBaseAddr->GetOpCode() == OP_dread) {
            AddrofNode *AddrOf = static_cast<AddrofNode *>(ElemBaseAddr);
            AddrOf->SetFieldID(Field);
          } else {
            ElemBaseAddr->Dump();
            ASSERT(false, "Unexpected base address in assignStruct");
          }
          assignArray(Res, ElemBaseAddr, ArrayTy, FieldNode);
        } else {
          assignStruct(Res, BaseSym, BaseAddr, StructTy, FieldNode, Field);
        }
      } else {
        if (WrapperNode->GetPrimType() == PTY_agg && FieldNode->GetPrimType() != PTY_agg) {
          MIRType *mirType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(WrapperNode->GetTyIdx());
          if (WrapperNode->GetFieldID() != 0) {
            TyIdx fldTyIdx = static_cast<MIRStructType *>(mirType)->GetFieldTyIdx(WrapperNode->GetFieldID());
            mirType = GlobalTables::GetTypeTable().GetTypeFromTyIdx(fldTyIdx);
          }
          if (mirType->GetKind() == kTypeArray) {
            continue;  // skip array field intiializing with a scalar constant
          }
        }
        StmtNode *FieldAssign;
        if (BaseSym) {
          FieldAssign = Builder->CreateStmtDassign(*BaseSym, Field, FieldNode);
        } else {
          FieldAssign = Builder->CreateStmtIassign(*StructPtrTy, Field,
                                                   BaseAddr, FieldNode);
        }
        FieldAssign->SetSrcPos(Pos);
        Res.appendStmtBefore(FieldAssign);
      }
    }
    return Size + 1;
  } else { // Direct assignment of the structure
    StmtNode *Assign;
    if (BaseSym) {
      Assign = Builder->CreateStmtDassign(*BaseSym, Base, Src);
    } else {
      Assign = Builder->CreateStmtIassign(*StructPtrTy, 0, BaseAddr, Src);
    }
    Assign->SetSrcPos(Pos);
    Res.appendStmtBefore(Assign);
    return StructTy->GetSize();
  }
}

BaseNode *Clang2MapleVisitor::getBase(MIRSymbol *Sym, BaseNode *Base,
                                      FieldID Field) {
  ASSERT(Sym || Base, "getBase requires weither Sym or Base to be set");
  if (Sym) {
    BaseNode *AddrOf = Builder->CreateExprAddrof(Field, *Sym);
    AddrOf->SetPrimType(PointerPrimTy);
    return AddrOf;
  } else {
    return Base->CloneTree(*Builder->GetCurrentFuncCodeMpAllocator());
  }
}

#ifdef MULTIDIM_ARRAYS
AddrofNode *Clang2MapleVisitor::computeArrayOffset(size_t &Offset,
                                                   ArrayNode *Array) {
  Offset = 0;
  MIRType *Ty = Array->GetArrayType(GlobalTables::GetTypeTable());
  ASSERT(Ty->GetKind() == kTypeArray, "Invalid indexing of non-array type");
  MIRArrayType *ArrTy = static_cast<MIRArrayType *>(Ty);
  size_t Multiplier = ArrTy->GetElemType()->GetSize();

  // Multiply for size of inner dimensions
  for (int m = ArrTy->GetDim() - 1; m >= Array->NumOpnds() - 1; m--) {
    Multiplier *= ArrTy->GetSizeArrayItem(m);
  }

  for (int i = Array->NumOpnds() - 1; i >= 1; i--) {
    BaseNode *IndexNode = Array->GetNopndAt(i);
    int64_t Val;
    if (!evaluateNodeAsInt(Val, IndexNode)) {
      return nullptr;
    }
    Offset += Val * Multiplier;
    Multiplier *= ArrTy->GetSizeArrayItem(i - 1);
  }

  BaseNode *Base = Array->GetBase();
  ASSERT(Base->GetOpCode() == OP_addrof, "Expected addrof node as array base");
  return static_cast<AddrofNode *>(Base);
}
#else  // !MULTIDIM_ARRAYS
AddrofNode *Clang2MapleVisitor::computeArrayOffset(size_t &Offset,
                                                   ArrayNode *Array) {
  Offset = 0;
  BaseNode *Base = Array;
  while (Base->GetOpCode() == OP_array) {
    Array = static_cast<ArrayNode *>(Base);
    MIRType *Ty = Array->GetArrayType(GlobalTables::GetTypeTable());
    ASSERT(Ty->GetKind() == kTypeArray, "Invalid indexing of non-array type");
    MIRArrayType *ArrTy = static_cast<MIRArrayType *>(Ty);
    MIRType *ElemTy = ArrTy->GetElemType();
    BaseNode *IndexNode = Array->GetNopndAt(1);
    int64_t Val;
    if (!evaluateNodeAsInt(Val, IndexNode)) {
      return nullptr;
    }
    Offset += Val * ElemTy->GetSize();

    Base = Array->GetBase();
  }

  ASSERT(Base->GetOpCode() == OP_addrof, "Expected addrof node as array base");
  return static_cast<AddrofNode *>(Base);
}
#endif // MULTIDIM_ARRAYS

BaseNode *Clang2MapleVisitor::getAddrOfNode(BaseNode *N) {
  switch (N->GetOpCode()) {
  case OP_dread:
    N->SetOpCode(OP_addrof);
    N->SetPrimType(PointerPrimTy);
    return N;
  case OP_iread:
    N->SetOpCode(OP_iaddrof);
    N->SetPrimType(PointerPrimTy);
    return N;
  default:
    ASSERT(false, "Unsupported node in addrOfNode");
    return nullptr;
  }
}

unsigned Clang2MapleVisitor::assignArray(Result &Res, BaseNode *BaseAddr,
                                         MIRArrayType *ArrayTy, BaseNode *Src) {
  MIRType *ElemTy = ArrayTy->GetElemType();
  size_t ElemSize = ElemTy->GetSize();
  MIRType *PtrTy = GlobalTables::GetTypeTable().GetOrCreatePointerType(
      *ElemTy, PointerPrimTy);
  SrcPosition Pos = sourceLocation2Mpl(Res.getLoc());
  size_t SizeInited = 0;
  if (Src->op == OP_constval) { // constant initializers
    ConstvalNode *ConstNode = static_cast<ConstvalNode *>(Src);
    MIRConst *Const = ConstNode->GetConstVal();
    if (Const->GetKind() == kConstAggConst) {
      MIRAggConst *Agg = static_cast<MIRAggConst *>(Const);
      for (unsigned ElemID = 0; ElemID < Agg->GetConstVec().size(); ++ElemID) {
        BaseNode *IndexNode =
            Builder->CreateIntConst(ElemID, IntPointerTy->GetPrimType());
        ArrayNode *Array;
#ifdef MULTIDIM_ARRAYS
        if (BaseAddr->GetOpCode() == OP_array) {
          Array = static_cast<ArrayNode *>(
              BaseAddr->CloneTree(*Builder->GetCurrentFuncCodeMpAllocator()));
          MapleVector<BaseNode *> &Opnds = Array->GetNopnd();
          Opnds.push_back(IndexNode);
          Array->SetNumOpnds(Opnds.size());
        } else {
#endif // MULTIDIM_ARRAYS
          Array = Builder->CreateExprArray(*ArrayTy, BaseAddr, IndexNode);
          Array->SetBoundsCheck(false);
          Array->SetPrimType(PointerPrimTy);
#ifdef MULTIDIM_ARRAYS
        }
#endif // MULTIDIM_ARRAYS
        MIRConst *ElemConst = Agg->GetConstVecItem(ElemID);
        if (ElemTy->GetKind() == kTypeArray) {
          MIRArrayType *ElemArrayTy = static_cast<MIRArrayType *>(ElemTy);
          ConstvalNode Elem(ElemConst);
          SizeInited += assignArray(Res, Array, ElemArrayTy, &Elem);
        } else if (ArrayTy->GetDim() > 1) {
          std::vector<maple::uint32> SizeArray;
          for (size_t d = 1; d < ArrayTy->GetDim(); d++) {
            SizeArray.push_back(ArrayTy->GetSizeArrayItem(d));
          }
          MIRArrayType NestedArrayTy(ElemTy->GetTypeIndex(), SizeArray);
          MIRArrayType *ElemArrayTy = static_cast<MIRArrayType *>(
              GlobalTables::GetTypeTable().GetOrCreateMIRTypeNode(
                  NestedArrayTy));
          ConstvalNode Elem(ElemConst);
          SizeInited += assignArray(Res, Array, ElemArrayTy, &Elem);
        } else if (ElemTy->GetKind() == kTypeStruct ||
                   ElemTy->GetKind() == kTypeUnion) {
          MIRStructType *ElemStructTy = static_cast<MIRStructType *>(ElemTy);
          MIRType *StructPtrTy =
              GlobalTables::GetTypeTable().GetOrCreatePointerType(
                  *ElemStructTy, PointerPrimTy);
          ConstvalNode Elem(ElemConst);
          BaseNode *StructBase =
              Builder->CreateExprIaddrof(*StructPtrTy, *StructPtrTy, 0, Array);
          assignStruct(Res, nullptr, StructBase, ElemStructTy, &Elem);
          SizeInited += ElemStructTy->GetSize();
        } else {
          BaseNode *Elem = constToNode(ElemConst);
          IassignNode *ElemAssign =
              Builder->CreateStmtIassign(*PtrTy, 0, Array, Elem);
          ElemAssign->SetSrcPos(Pos);
          Res.appendStmtBefore(ElemAssign);
          SizeInited += ElemSize;
        }
      }
    } else if (Const->GetKind() == kConstInt) {
      // Use memset to set the whole array to the specified value
      MIRIntConst *IConst = static_cast<MIRIntConst *>(Const);
      MapleVector<BaseNode *> MemsetArgs(
          Builder->GetCurrentFuncCodeMpAllocator()->Adapter());
      MemsetArgs.push_back(BaseAddr);
      BaseNode *Value = Builder->CreateIntConst(IConst->GetValue().GetZXTValue(), PTY_i32);
      MemsetArgs.push_back(Value);
      BaseNode *Size = Builder->CreateIntConst(ArrayTy->GetSize(),
                                               IntPointerTy->GetPrimType());
      MemsetArgs.push_back(Size);
      StmtNode *MemsetCall =
          Builder->CreateStmtIntrinsicCall(INTRN_C_memset, MemsetArgs);
      MemsetCall->SetSrcPos(Pos);
      Res.appendStmtBefore(MemsetCall);
      SizeInited += ArrayTy->GetSize();
    } else {
      LogInfo::MapleLogger()
          << "Error: Unhandled aggregate initializer in assignArray\n";
#ifdef DEBUG
      Const->Dump();
#endif
      CHECK_FATAL(false, "Failing due to unsupported feature");
    }
  } else if (Src->op == OP_conststr) {
    // Generate a memcpy to copy the string literal into the local variable
    ConststrNode *ConstStr = static_cast<ConststrNode *>(Src);
    UStrIdx SIdx = ConstStr->GetStrIdx();
    std::string Str = GlobalTables::GetUStrTable().GetStringFromStrIdx(SIdx);

    MapleVector<BaseNode *> MemcpyArgs(
        Builder->GetCurrentFuncCodeMpAllocator()->Adapter());
    MemcpyArgs.push_back(BaseAddr);
    MemcpyArgs.push_back(ConstStr);
    MemcpyArgs.push_back(Builder->GetConstInt(Str.size() + 1));

    StmtNode *MemcpyCall =
        Builder->CreateStmtIntrinsicCall(INTRN_C_memcpy, MemcpyArgs);
    MemcpyCall->SetSrcPos(Pos);
    Res.appendStmtBefore(MemcpyCall);
    SizeInited = Str.size() + 1;
  } else if (Src->op == OP_intrinsicopwithtype) { // non-constant initializers
    NaryNode *N = static_cast<NaryNode *>(Src);
    for (size_t ElemID = 0; ElemID < N->NumOpnds(); ++ElemID) {
      BaseNode *IndexNode =
          Builder->CreateIntConst(ElemID, IntPointerTy->GetPrimType());
      ArrayNode *Array =
          Builder->CreateExprArray(*ArrayTy, BaseAddr, IndexNode);
      Array->SetBoundsCheck(false);
      Array->SetPrimType(PointerPrimTy);
      if (ElemTy->GetKind() == kTypeArray) {
        MIRArrayType *ElemArrayTy = static_cast<MIRArrayType *>(ElemTy);
        SizeInited += assignArray(Res, Array, ElemArrayTy, N->Opnd(ElemID));
      } else if (ArrayTy->GetDim() > 1) {
        std::vector<maple::uint32> SizeArray;
        for (size_t d = 1; d < ArrayTy->GetDim(); d++) {
          SizeArray.push_back(ArrayTy->GetSizeArrayItem(d));
        }
        MIRArrayType NestedArrayTy(ElemTy->GetTypeIndex(), SizeArray);
        MIRArrayType *ElemArrayTy = static_cast<MIRArrayType *>(
            GlobalTables::GetTypeTable().GetOrCreateMIRTypeNode(NestedArrayTy));
        SizeInited += assignArray(Res, Array, ElemArrayTy, N->Opnd(ElemID));
      } else if (ElemTy->GetKind() == kTypeStruct ||
                 ElemTy->GetKind() == kTypeUnion) {
        MIRStructType *ElemStructTy = static_cast<MIRStructType *>(ElemTy);
        MIRType *StructPtrTy =
            GlobalTables::GetTypeTable().GetOrCreatePointerType(*ElemStructTy,
                                                                PointerPrimTy);
        BaseNode *StructBase =
            Builder->CreateExprIaddrof(*StructPtrTy, *StructPtrTy, 0, Array);
        assignStruct(Res, nullptr, StructBase, ElemStructTy, N->Opnd(ElemID));
        SizeInited += ElemStructTy->GetSize();
      } else {
        IassignNode *ElemAssign =
            Builder->CreateStmtIassign(*PtrTy, 0, Array, N->Opnd(ElemID));
        ElemAssign->SetSrcPos(Pos);
        Res.appendStmtBefore(ElemAssign);
        SizeInited += ElemSize;
      }
    }
  } else {
    LogInfo::MapleLogger()
        << "Error: Unhandled aggregate initializer in assignArray\n";
#ifdef DEBUG
    Src->Dump();
#endif
    CHECK_FATAL(false, "Failing due to unsupported feature");
  }

  // Fill in uninitialized elements with 0 using memset
  if (SizeInited < ArrayTy->GetSize()) {
    MapleVector<BaseNode *> MemsetArgs(
        Builder->GetCurrentFuncCodeMpAllocator()->Adapter());
    BaseNode *Offset = Builder->CreateIntConst(SizeInited, PointerPrimTy);
    BaseNode *Base =
        Builder->CreateExprBinary(OP_add, *IntPointerTy, BaseAddr, Offset);
    MemsetArgs.push_back(Base);
    BaseNode *Zero = Builder->CreateIntConst(0, PTY_i32);
    MemsetArgs.push_back(Zero);
    BaseNode *SizeLeft = Builder->CreateIntConst(
        ArrayTy->GetSize() - SizeInited, IntPointerTy->GetPrimType());
    MemsetArgs.push_back(SizeLeft);
    StmtNode *MemsetCall =
        Builder->CreateStmtIntrinsicCall(INTRN_C_memset, MemsetArgs);
    MemsetCall->SetSrcPos(Pos);
    Res.appendStmtBefore(MemsetCall);
  }

  return ArrayTy->GetSize();
}

unsigned Clang2MapleVisitor::assignVector(Result &Res, MIRSymbol *VecSym,
                                          MIRType *Ty, BaseNode *Src) {
  Result VecRes(VecSym, Ty, Res.getLoc());
  SrcPosition Pos = sourceLocation2Mpl(Res.getLoc());
  size_t SizeInited = 0;
  if (Src->op == OP_constval) { // constant initializers
    ConstvalNode *ConstNode = static_cast<ConstvalNode *>(Src);
    MIRConst *Const = ConstNode->GetConstVal();
    if (Const->GetKind() == kConstAggConst) {
      MIRAggConst *Agg = static_cast<MIRAggConst *>(Const);
      for (unsigned ElemID = 0; ElemID < Agg->GetConstVec().size(); ++ElemID) {
        BaseNode *IndexNode =
            Builder->CreateIntConst(ElemID, IntPointerTy->GetPrimType());
        MIRConst *ElemConst = Agg->GetConstVecItem(ElemID);
        BaseNode *Elem = constToNode(ElemConst);

        StmtNode *ElemAssign = vectorSetLane(Ty, VecRes, IndexNode, Elem);
        ElemAssign->SetSrcPos(Pos);
        Res.appendStmtBefore(ElemAssign);
        SizeInited += 1;
      }
    }
  } else if (Src->op == OP_intrinsicopwithtype) { // non-constant initializers
    NaryNode *N = static_cast<NaryNode *>(Src);
    for (size_t ElemID = 0; ElemID < N->NumOpnds(); ++ElemID) {
      BaseNode *IndexNode =
          Builder->CreateIntConst(ElemID, IntPointerTy->GetPrimType());

      StmtNode *ElemAssign =
          vectorSetLane(Ty, VecRes, IndexNode, N->Opnd(ElemID));
      ElemAssign->SetSrcPos(Pos);
      Res.appendStmtBefore(ElemAssign);
      SizeInited += 1;
    }
  } else {
    StmtNode *Init = Builder->CreateStmtDassign(*VecSym, 0, Src);
    Res.appendStmtBefore(Init);
    SizeInited += Ty->GetSize();
  }

  return SizeInited;
}

StmtNode *Clang2MapleVisitor::vectorSetLane(MIRType *Ty, Result &VecRes,
                                            BaseNode *Index, BaseNode *Src) {
  MapleVector<BaseNode *> Args(
      Builder->GetCurrentFuncCodeMpAllocator()->Adapter());
  Args.push_back(Src);
  Args.push_back(getNodeAsRVal(VecRes));
  Args.push_back(Index);

  MIRIntrinsicID Intrinsic;
  switch (Ty->GetPrimType()) {
#define SETQ_LANE(TY)                                                          \
  case PTY_##TY:                                                               \
    Intrinsic = INTRN_vector_set_element_##TY;                                 \
    break;

    SETQ_LANE(v2i64)
    SETQ_LANE(v4i32)
    SETQ_LANE(v8i16)
    SETQ_LANE(v16i8)
    SETQ_LANE(v2u64)
    SETQ_LANE(v4u32)
    SETQ_LANE(v8u16)
    SETQ_LANE(v16u8)
    SETQ_LANE(v2f64)
    SETQ_LANE(v4f32)
    SETQ_LANE(v2i32)
    SETQ_LANE(v4i16)
    SETQ_LANE(v8i8)
    SETQ_LANE(v2u32)
    SETQ_LANE(v4u16)
    SETQ_LANE(v8u8)
    SETQ_LANE(v2f32)
  case PTY_i64:
    Intrinsic = INTRN_vector_set_element_v1i64;
    break;
  case PTY_u64:
    Intrinsic = INTRN_vector_set_element_v1u64;
    break;
  case PTY_f64:
    Intrinsic = INTRN_vector_set_element_v1f64;
    break;
  default:
    LogInfo::MapleLogger() << "Error: Unhandled vector type in vectorSetLane\n";
#ifdef DEBUG
    Ty->Dump(0);
#endif
    CHECK_FATAL(false, "Failing due to unsupported feature");
    return nullptr;
  }

  BaseNode *CallIntrinsic =
      Builder->CreateExprIntrinsicop(Intrinsic, OP_intrinsicop, *Ty, Args);
  StmtNode *Assign;
  if (VecRes.isDeref()) {
    Assign = Builder->CreateStmtIassign(*VecRes.getAddrTy(), VecRes.getField(),
                                        VecRes.getAddr(), CallIntrinsic);
  } else if (VecRes.isSym()) {
    Assign = Builder->CreateStmtDassign(*VecRes.getSym(), VecRes.getField(),
                                        CallIntrinsic);
  } else {
    ASSERT(false, "Error: Destination vector is neither deref or symbol");
  }

  return Assign;
}

PrimType Clang2MapleVisitor::getVectorElementPrimType(PrimType VectorPrimType) {
  switch (VectorPrimType) {

  case PTY_v2i64:
    return PTY_i64;
  case PTY_v4i32:
  case PTY_v2i32:
    return PTY_i32;
  case PTY_v8i16:
  case PTY_v4i16:
    return PTY_i16;
  case PTY_v16i8:
  case PTY_v8i8:
    return PTY_i8;
  case PTY_v2u64:
    return PTY_u64;
  case PTY_v4u32:
  case PTY_v2u32:
    return PTY_u32;
  case PTY_v8u16:
  case PTY_v4u16:
    return PTY_u16;
  case PTY_v16u8:
  case PTY_v8u8:
    return PTY_u8;
  case PTY_v2f64:
    return PTY_f64;
  case PTY_v4f32:
  case PTY_v2f32:
    return PTY_f32;
  case PTY_i64:
    return PTY_i64;
  case PTY_u64:
    return PTY_u64;
  case PTY_f64:
    return PTY_f64;
  default:
    LogInfo::MapleLogger()
        << "Error: Unhandled vector type in getVectorElementPrimType: "
        << VectorPrimType << "\n";
    CHECK_FATAL(false, "Failing due to unsupported feature");
    return PTY_unknown;
  }
}

// TODO: This function has a lot of duplication and may miss some possible
// combinations. It should be improved, possibly using some recursion to
// evaluate sub-expressions.
MIRConst *Clang2MapleVisitor::getInitializer(MIRType *Ty,
                                             const clang::Expr *InitExpr) {
  Result InitRes = Visit(InitExpr);
  BaseNode *InitNode = getNodeAsRVal(InitRes);
  // This case should only happen with an array of size 0
  if (!InitNode) {
    return nullptr;
  }

  if (InitNode->op == OP_constval) {
    ConstvalNode *ConstNode = static_cast<ConstvalNode *>(InitNode);

    // For array types, if there are less initializers than elements of the
    // array, we need to fill in the rest.
    MIRConst *Const = ConstNode->GetConstVal();
    if (Ty->GetKind() == kTypeArray) {
      MIRArrayType *ArrTy = static_cast<MIRArrayType *>(Ty);
      if (Const->GetKind() == kConstAggConst) {
        MIRAggConst *Agg = static_cast<MIRAggConst *>(Const);
        completeArrayInitializer(Agg, ArrTy);
      }
    }

    // For bitfields, assign the corresponding bitfield types to the
    // initializers.
    if (Ty->IsMIRStructType() && Const->GetKind() == kConstAggConst) {
      MIRStructType *StructTy = static_cast<MIRStructType *>(Ty);
      MIRAggConst *Agg = static_cast<MIRAggConst *>(Const);

      for (unsigned Item = 0; Item < Agg->GetConstVec().size(); Item++) {
        MIRType *FieldTy = StructTy->GetElemType(Agg->GetFieldIdItem(Item) - 1);
        if (FieldTy->GetKind() == kTypeBitField) {
          MIRConst *FieldConst = Agg->GetConstVecItem(Item);
          ASSERT(FieldConst->GetKind() == kConstInt,
                 "bitfield initializer must be an integer");
          MIRIntConst *FieldInit = static_cast<MIRIntConst *>(FieldConst);
          Agg->SetItem(Item,
                       GlobalTables::GetIntConstTable().GetOrCreateIntConst(
                           FieldInit->GetValue().GetZXTValue(), *FieldTy),
                       Agg->GetFieldIdItem(Item));
        }
      }
    }

    return Const;
  } else if (InitNode->op == OP_conststr) {
    ConststrNode *ConstStr = static_cast<ConststrNode *>(InitNode);
    UStrIdx SIdx = ConstStr->GetStrIdx();
    if (Ty->GetKind() == kTypeArray) {
      MIRArrayType *ArrTy = static_cast<MIRArrayType *>(Ty);
      // Expand the string to an array of characters
      std::string Str = GlobalTables::GetUStrTable().GetStringFromStrIdx(SIdx);
      MIRAggConst *Agg =
          Builder->GetCurrentFuncCodeMp()->New<MIRAggConst>(*Module, *Ty);
      int Remaining = ArrTy->GetSizeArrayItem(0);
      for (char &C : Str) {
        Agg->PushBack(GlobalTables::GetIntConstTable().GetOrCreateIntConst(
            C, *ArrTy->GetElemType()));
        if (--Remaining == 0)
          break;
      }

      // Add the terminator, if it fits
      if (Remaining > 0) {
        Agg->PushBack(createZero(ArrTy->GetElemType()));
      }

      return Agg;
    } else {
      return Builder->GetCurrentFuncCodeMp()->New<MIRStrConst>(SIdx, *Ty);
    }
  } else if (InitNode->op == OP_addrof) {
    AddrofNode *AddrOf = static_cast<AddrofNode *>(InitNode);
    return Builder->GetCurrentFuncCodeMp()->New<MIRAddrofConst>(
        AddrOf->GetStIdx(), AddrOf->GetFieldID(), *Ty);
  } else if (InitNode->op == OP_addroflabel) {
    AddroflabelNode *AddrOf = static_cast<AddroflabelNode *>(InitNode);
    return Builder->GetCurrentFuncCodeMp()->New<MIRLblConst>(
        AddrOf->GetOffset(), Builder->GetCurrentFunction()->GetPuidx(), *Ty);
  } else if (InitNode->op == OP_addroffunc) {
    AddroffuncNode *AOF = static_cast<AddroffuncNode *>(InitNode);
    return Builder->GetCurrentFuncCodeMp()->New<MIRAddroffuncConst>(
        AOF->GetPUIdx(), *Ty);
  } else if (InitNode->op == OP_iaddrof) {
    IaddrofNode *IAddrOf = static_cast<IaddrofNode *>(InitNode);
    BaseNode *Base = IAddrOf->Opnd(0);

    // Find the offset of the field.
    size_t FieldOffset = 0;
    if (IAddrOf->GetFieldID()) {
      auto UO =
          llvm::dyn_cast<clang::UnaryOperator>(InitExpr->IgnoreParenCasts());
      ASSERT(UO, "Expected unary operator");
      auto ME = llvm::dyn_cast<clang::MemberExpr>(
          UO->getSubExpr()->IgnoreParenCasts());
      ASSERT(ME, "Expected member expression");
      uint64_t OffsetBits = Context->getFieldOffset(ME->getMemberDecl());
      FieldOffset += (OffsetBits / BITS_PER_BYTE);
    }

    // Handle an address of an array element, e.g. &x[4]
    if (Base->GetOpCode() == OP_array) {
      ArrayNode *Array = static_cast<ArrayNode *>(Base);
      BaseNode *ArrayBase = Array->GetBase();
      if (ArrayBase->GetOpCode() == OP_addrof) {
        size_t ArrayOffset = 0;
        AddrofNode *ArrayAddr = computeArrayOffset(ArrayOffset, Array);
        ASSERT(ArrayAddr, "Unable to compute array offset");
        return Builder->GetCurrentFuncCodeMp()->New<MIRAddrofConst>(
            ArrayAddr->GetStIdx(), ArrayAddr->GetFieldID(), *Ty,
            ArrayOffset + FieldOffset);
      } else if (ArrayBase->GetOpCode() == OP_conststr) {
        ConststrNode *ConstStr = static_cast<ConststrNode *>(ArrayBase);
        ASSERT(Array->NumOpnds() == 2,
               "Invalid array access on string constant");
        BaseNode *IndexNode = Array->GetNopndAt(1);
        int64_t Val;
        bool Safe = evaluateNodeAsInt(Val, IndexNode);
        ASSERT(Safe, "Unable to determine array offset");
        const std::string &InitialString =
            GlobalTables::GetUStrTable().GetStringFromStrIdx(
                ConstStr->GetStrIdx());
        return Builder->GetCurrentFuncCodeMp()->New<MIRStrConst>(
            InitialString.substr(Val), *Ty);
      }
    } else if (Base->GetOpCode() == OP_constval) {
      // This handles the case where the code is attempting to get the offset
      // of a structure field by using the following type of expression:
      //    &((struct s *)0)->b)
      if (auto UO = llvm::dyn_cast<clang::UnaryOperator>(
              InitExpr->IgnoreParenCasts())) {
        if (auto ME = llvm::dyn_cast<clang::MemberExpr>(
                UO->getSubExpr()->IgnoreParenCasts())) {
          ConstvalNode *ConstNode = static_cast<ConstvalNode *>(Base);
          MIRConst *Const = ConstNode->GetConstVal();
          ASSERT(Const->GetKind() == kConstInt,
                 "Only an integer is allowed here");
          MIRIntConst *IntConst = static_cast<MIRIntConst *>(Const);
          int64_t Base = IntConst->GetValue().GetSXTValue();
          uint64_t OffsetBits = Context->getFieldOffset(ME->getMemberDecl());
          return GlobalTables::GetIntConstTable().GetOrCreateIntConst(
              Base + OffsetBits / BITS_PER_BYTE, *Ty);
        }
      }
    } else if (Base->GetOpCode() == OP_addrof) {
      AddrofNode *AddrOf = static_cast<AddrofNode *>(Base);
      return Builder->GetCurrentFuncCodeMp()->New<MIRAddrofConst>(
          AddrOf->GetStIdx(), AddrOf->GetFieldID(), *Ty, FieldOffset);
    } else if (Base->GetOpCode() == OP_add) {
      BinaryNode *AddNode = static_cast<BinaryNode *>(Base);
      BaseNode *LhsNode = AddNode->GetBOpnd(0);
      BaseNode *RhsNode = AddNode->GetBOpnd(1);
      if (LhsNode->GetOpCode() == OP_addrof &&
          RhsNode->GetOpCode() == OP_constval) {
        AddrofNode *AddrOf = static_cast<AddrofNode *>(LhsNode);
        ConstvalNode *RhsConst = static_cast<ConstvalNode *>(RhsNode);
        ASSERT(RhsConst->GetConstVal()->GetKind() == kConstInt,
               "expected int const");
        MIRIntConst *IntConst =
            static_cast<MIRIntConst *>(RhsConst->GetConstVal());
        int64_t Val = IntConst->GetValue().GetSXTValue();
        return Builder->GetCurrentFuncCodeMp()->New<MIRAddrofConst>(
            AddrOf->GetStIdx(), AddrOf->GetFieldID(), *Ty, Val + FieldOffset);
      } else if (RhsNode->GetOpCode() == OP_addrof &&
                 LhsNode->GetOpCode() == OP_constval) {
        AddrofNode *AddrOf = static_cast<AddrofNode *>(RhsNode);
        ConstvalNode *LhsConst = static_cast<ConstvalNode *>(LhsNode);
        ASSERT(LhsConst->GetConstVal()->GetKind() == kConstInt,
               "expected int const");
        MIRIntConst *IntConst =
            static_cast<MIRIntConst *>(LhsConst->GetConstVal());
        int64_t Val = IntConst->GetValue().GetSXTValue();
        return Builder->GetCurrentFuncCodeMp()->New<MIRAddrofConst>(
            AddrOf->GetStIdx(), AddrOf->GetFieldID(), *Ty, Val + FieldOffset);
      }
    }
  } else if (InitNode->GetOpCode() == OP_array) {
    ArrayNode *Array = static_cast<ArrayNode *>(InitNode);
    BaseNode *ArrayBase = Array->GetBase();
    if (ArrayBase->GetOpCode() == OP_conststr) {
      ConststrNode *ConstStr = static_cast<ConststrNode *>(ArrayBase);
      ASSERT(Array->NumOpnds() == 2, "Invalid array access on string constant");
      BaseNode *IndexNode = Array->GetNopndAt(1);
      int64_t Val;
      bool Safe = evaluateNodeAsInt(Val, IndexNode);
      ASSERT(Safe, "Unable to determine array offset");
      const std::string &InitialString =
          GlobalTables::GetUStrTable().GetStringFromStrIdx(
              ConstStr->GetStrIdx());
      return Builder->GetCurrentFuncCodeMp()->New<MIRStrConst>(
          InitialString.substr(Val), *Ty);
    } else {
      size_t ArrayOffset = 0;
      AddrofNode *ArrayAddr = computeArrayOffset(ArrayOffset, Array);
      ASSERT(ArrayAddr, "Unable to compute array offset");
      return Builder->GetCurrentFuncCodeMp()->New<MIRAddrofConst>(
          ArrayAddr->GetStIdx(), ArrayAddr->GetFieldID(), *Ty, ArrayOffset);
    }
  } else if (InitNode->GetOpCode() == OP_add) {
    BinaryNode *Bin = static_cast<BinaryNode *>(InitNode);
    BaseNode *Lhs = Bin->GetBOpnd(0);
    BaseNode *Rhs = Bin->GetBOpnd(1);
    // Look for this type of expression: "foo" + 1
    if (Lhs->GetOpCode() == OP_conststr && Rhs->GetOpCode() == OP_constval) {
      ConststrNode *LhsConstStr = static_cast<ConststrNode *>(Lhs);
      ConstvalNode *RhsConstNode = static_cast<ConstvalNode *>(Rhs);
      MIRConst *RhsConst = RhsConstNode->GetConstVal();
      ASSERT(RhsConst->GetKind() == kConstInt, "Expected int offset");
      MIRIntConst *IntConst = static_cast<MIRIntConst *>(RhsConst);

      std::string Str = GlobalTables::GetUStrTable().GetStringFromStrIdx(
          LhsConstStr->GetStrIdx());
      int64_t Offset = IntConst->GetValue().GetSXTValue();
      ASSERT(Offset >= 0 && Offset < (int64_t)Str.length(),
             "Invalid offset beyond length of string constant");
      return Builder->GetCurrentFuncCodeMp()->New<MIRStrConst>(
          Str.substr(Offset), *Ty);
    }
  }

  if (MIRConst *ConstVal = evaluateExprAsConst(InitExpr, Ty)) {
    return ConstVal;
  }

  LogInfo::MapleLogger()
      << "Error: Unable to evaluate initializer as a constant\n";
  InitNode->Dump();
  return nullptr;
}

MIRConst *Clang2MapleVisitor::expandConstants(MIRType *Ty, MIRConst *Const) {
  if (Ty->IsStructType()) {
    MIRStructType *StructTy = static_cast<MIRStructType *>(Ty);
    if (Const->GetKind() == kConstAggConst) {
      MIRAggConst *Agg = static_cast<MIRAggConst *>(Const);
      for (size_t Index = 0, ItemIndex = 0; Index < StructTy->GetFieldsSize();
           Index++) {
        FieldID Field = Index + 1;
        if (StructTy->GetKind() == kTypeUnion && Agg->GetConstVec().size() <= Index) {
          continue;
        }
        MIRType *FieldTy = StructTy->GetElemType(Index);
        if (MIRConst *FieldConst = Agg->GetAggConstElement(Field)) {
          Agg->SetItem(ItemIndex++, expandConstants(FieldTy, FieldConst),
                       Field);
        }
      }
      return Agg;
    } else {
      LogInfo::MapleLogger()
          << "Error: Unhandled constant in expandConstants\n";
#ifdef DEBUG
      Const->Dump();
#endif
      CHECK_FATAL(false, "Failing due to unsupported feature");
    }
  } else if (Ty->GetKind() == kTypeArray) {
    MIRArrayType *ArrTy = static_cast<MIRArrayType *>(Ty);
    if (Const->GetKind() == kConstAggConst) {
      MIRAggConst *Agg = static_cast<MIRAggConst *>(Const);
      MapleVector<MIRConst *> &Items = Agg->GetConstVec();
      MIRType *ElemTy;
#ifdef MULTIDIM_ARRAYS
      if (ArrTy->GetDim() > 1) {
        std::vector<maple::uint32> SizeArray;
        for (size_t d = 1; d < ArrTy->GetDim(); d++) {
          SizeArray.push_back(ArrTy->GetSizeArrayItem(d));
        }
        MIRArrayType NewArrTy(ArrTy->GetElemTyIdx(), SizeArray);
        ElemTy = static_cast<MIRArrayType *>(
            GlobalTables::GetTypeTable().GetOrCreateMIRTypeNode(NewArrTy));
      } else {
#endif // MULTIDIM_ARRAYS
        ElemTy = ArrTy->GetElemType();
#ifdef MULTIDIM_ARRAYS
      }
#endif // MULTIDIM_ARRAYS
      size_t Index;
      for (Index = 0; Index < Items.size(); Index++) {
        MIRConst *ElemConst = Agg->GetConstVecItem(Index);
        Items[Index] = expandConstants(ElemTy, ElemConst);
      }

      return Agg;
    } else if (Const->GetKind() == kConstInt) {
      MIRAggConst *Agg =
          Builder->GetCurrentFuncDataMp()->New<MIRAggConst>(*Module, *Ty);
      for (size_t i = 0; i < ArrTy->GetSizeArrayItem(0); i++) {
        Agg->PushBack(Const);
      }
      return Agg;
    }
  }

  return Const;
}

MIRConst *Clang2MapleVisitor::completeArrayInitializer(MIRAggConst *Agg,
                                                       MIRArrayType *ArrTy) {
  MIRType *ElemTy;
#ifdef MULTIDIM_ARRAYS
  if (ArrTy->GetDim() > 1) {
    std::vector<maple::uint32> SizeArray;
    for (size_t d = 1; d < ArrTy->GetDim(); d++) {
      SizeArray.push_back(ArrTy->GetSizeArrayItem(d));
    }
    MIRArrayType NewArrTy(ArrTy->GetElemTyIdx(), SizeArray);
    ElemTy = static_cast<MIRArrayType *>(
        GlobalTables::GetTypeTable().GetOrCreateMIRTypeNode(NewArrTy));
  } else {
#endif // MULTIDIM_ARRAYS
    ElemTy = ArrTy->GetElemType();
#ifdef MULTIDIM_ARRAYS
  }
#endif // MULTIDIM_ARRAYS

  // First, complete any partially initialized inner arrays
  for (size_t Elem = 0; Elem < Agg->GetConstVec().size(); Elem++) {
    MIRConst *Const = Agg->GetConstVecItem(Elem);
    if (Const->GetKind() == kConstAggConst) {
      // An agg is also used for a struct, but we only care about arrays here
      if (ElemTy->GetKind() == kTypeArray) {
        completeArrayInitializer(static_cast<MIRAggConst *>(Const),
                                 static_cast<MIRArrayType *>(ElemTy));
      }
    }
  }

  // Then complete this level of the array
  MIRConst *Zero = createZero(ElemTy);
  for (size_t Inited = Agg->GetConstVec().size(),
              ArraySize = ArrTy->GetSizeArrayItem(0);
       Inited < ArraySize; ++Inited) {
    Agg->PushBack(Zero);
  }

  return Agg;
}

MIRType *Clang2MapleVisitor::isHomogenousAggregate(MIRType *Ty) {
  MIRType *ElemTy = nullptr;
  if (Ty->IsStructType()) {
    MIRStructType *StructTy = static_cast<MIRStructType *>(Ty);
    for (auto FieldPair : StructTy->GetFields()) {
      MIRType *FieldTy =
          GlobalTables::GetTypeTable().GetTypeFromTyIdx(FieldPair.second.first);
      if (FieldTy->GetKind() == kTypeArray) {
        MIRArrayType *ArrayTy = static_cast<MIRArrayType *>(FieldTy);
        MIRType *ArrayElemTy = ArrayTy->GetElemType();
        if (!PrimitiveType(ArrayElemTy->GetPrimType()).IsFloat()) {
          return nullptr;
        }
        FieldTy = ArrayElemTy;
      } else if (!PrimitiveType(FieldTy->GetPrimType()).IsFloat()) {
        return nullptr;
      }

      if (!ElemTy) {
        ElemTy = FieldTy;
      } else if (ElemTy != FieldTy) {
        return nullptr;
      }
    }
  }

  return ElemTy;
}

bool Clang2MapleVisitor::passInGeneralRegisters(MIRType *Ty) {
  if (Ty->GetSize() > 16)
    return false;
  if (PrimitiveType(Ty->GetPrimType()).IsFloat())
    return false;
  if (isHomogenousAggregate(Ty))
    return false;
  return true;
}

bool Clang2MapleVisitor::passInFloatRegisters(MIRType *Ty) {
  if (Ty->GetSize() > 16)
    return false;
  return PrimitiveType(Ty->GetPrimType()).IsFloat();
}

bool Clang2MapleVisitor::shouldEvaluateArgs(const clang::FunctionDecl *FD) {
  if (!FD)
    return true;
  switch (FD->getBuiltinID()) {
  case clang::Builtin::BI__builtin_constant_p:
  case clang::Builtin::BI__builtin_classify_type:
  case clang::Builtin::BI__builtin_object_size:
  case clang::Builtin::BI__builtin_dynamic_object_size:
    return false;
  default:
    return true;
  }
}

bool Clang2MapleVisitor::isNoCvtNeeded(PrimType ToPTy, PrimType FromPTy) {
  if (ToPTy == FromPTy || ToPTy == GetRegPrimType(FromPTy))
    return true;
  switch (ToPTy) {
  case PTY_i32:
    return FromPTy == PTY_i16 || FromPTy == PTY_i8;
  case PTY_u32:
    return FromPTy == PTY_u16 || FromPTy == PTY_u8 || FromPTy == PTY_a32 ||
           (PointerPrimTy == PTY_a32 && FromPTy == PTY_ptr);
  case PTY_a32:
    return FromPTy == PTY_u32;
  case PTY_i64:
    return FromPTy == PTY_i32;
  case PTY_u64:
    return FromPTy == PTY_u32 || FromPTy == PTY_a64 ||
           (PointerPrimTy == PTY_a64 && FromPTy == PTY_ptr);
  case PTY_a64:
    return FromPTy == PTY_u64;
  case PTY_ptr:
    return FromPTy == PointerPrimTy;
  default:
    return false;
  }
}

bool Clang2MapleVisitor::isPointerIntConvert(PrimType ToPTy, PrimType FromPTy) {
  switch (ToPTy) {
  case PTY_u32:
    return FromPTy == PTY_a32 ||
           (PointerPrimTy == PTY_a32 && FromPTy == PTY_ptr);
  case PTY_a32:
    return FromPTy == PTY_u32;
  case PTY_u64:
    return FromPTy == PTY_a64 ||
           (PointerPrimTy == PTY_a64 && FromPTy == PTY_ptr);
  case PTY_a64:
    return FromPTy == PTY_u64;
  case PTY_ptr:
    return FromPTy == PointerPrimTy;
  default:
    return false;
  }
}

static MIRType *getIntType(unsigned BitSize, bool Signed) {
  PrimType PTy;
  switch (BitSize) {
  case 8:
    PTy = Signed ? PTY_i8 : PTY_u8;
    break;
  case 16:
    PTy = Signed ? PTY_i16 : PTY_u16;
    break;
  case 32:
    PTy = Signed ? PTY_i32 : PTY_u32;
    break;
  case 64:
    PTy = Signed ? PTY_i64 : PTY_u64;
    break;
  default:
    ASSERT(false, "unexpected BitSize in GetPrimType");
  }
  return GlobalTables::GetTypeTable().GetPrimType(PTy);
}

BaseNode *Clang2MapleVisitor::createNumericCast(MIRType *ToTy, BaseNode *N) {
  PrimType FromPTy = N->GetPrimType();
  PrimType ToPTy = ToTy->GetPrimType();

  if (FromPTy == ToPTy || isPointerIntConvert(FromPTy, ToPTy))
    return N;

  if (PrimitiveType(FromPTy).IsInteger() && PrimitiveType(ToPTy).IsInteger()) {
    if (N->GetOpCode() == OP_constval) {
      N->SetPrimType(GetRegPrimType(ToPTy));
      return N;
    }

    if (isNoCvtNeeded(ToPTy, FromPTy) &&
        (N->GetOpCode() == OP_dread || N->GetOpCode() == OP_iread)) {
      N->SetPrimType(GetRegPrimType(ToPTy));
      return N;
    }

    // Only generate sext/zext for < 32-bit types
    if (GetPrimTypeSize(ToPTy) < 4 || GetPrimTypeSize(FromPTy) < 4) {
      if (GetPrimTypeSize(ToPTy) <= GetPrimTypeSize(FromPTy)) {
        if (PrimitiveType(ToPTy).IsUnsigned()) {
          return Builder->CreateExprExtractbits(
              OP_zext, *getIntType(GetPrimTypeBitSize(FromPTy), false), 0,
              GetPrimTypeBitSize(ToPTy), N);
        } else {
          return Builder->CreateExprExtractbits(
              OP_sext, *getIntType(GetPrimTypeBitSize(FromPTy), true), 0,
              GetPrimTypeBitSize(ToPTy), N);
        }
      }

      // GetPrimTypeSize(FromPTy) < GetPrimTypeSize(ToPTy)
      if (PrimitiveType(FromPTy).IsUnsigned()) {
        return Builder->CreateExprExtractbits(
            OP_zext, *getIntType(GetPrimTypeBitSize(ToPTy), false), 0,
            GetPrimTypeBitSize(FromPTy), N);
      } else {
        return Builder->CreateExprExtractbits(
            OP_sext, *getIntType(GetPrimTypeBitSize(ToPTy), true), 0,
            GetPrimTypeBitSize(FromPTy), N);
      }
    }
  }

  return Builder->CreateExprTypeCvt(OP_cvt, ToPTy, FromPTy, *N);
}

std::unique_ptr<clang::tooling::FrontendActionFactory>
clang2MapleFrontendActionFactory() {
  class Clang2MapleFrontendActionFactory
      : public clang::tooling::FrontendActionFactory {
  public:
    Clang2MapleFrontendActionFactory() {}

    std::unique_ptr<clang::FrontendAction> create() override {
      return std::make_unique<Clang2MapleAction>();
    }
  };

  return std::unique_ptr<clang::tooling::FrontendActionFactory>(
      new Clang2MapleFrontendActionFactory());
}

static MIRStorageClass StorageClass2Mpl(clang::StorageClass SC) {
  switch (SC) {
  case clang::SC_None:
    return kScGlobal; // FIXME: Not sure what should return here
  case clang::SC_Extern:
  case clang::SC_PrivateExtern:
    return kScExtern;
  case clang::SC_Static:
    return kScFstatic; // FIXME: Need to differentiate between pstatic and
                       // fstatic
  case clang::SC_Auto:
    return kScAuto;
  case clang::SC_Register:
    return kScInvalid; // FIXME: Find the correct alternative for this
  }
  return kScInvalid;
}

static Opcode BinOpcode2Mpl(clang::BinaryOperatorKind Op, bool isSigned) {
  switch (Op) {
  case clang::BO_Mul:
  case clang::BO_MulAssign:
    return OP_mul;
  case clang::BO_Div:
  case clang::BO_DivAssign:
    return OP_div;
  case clang::BO_Rem:
  case clang::BO_RemAssign:
    return OP_rem;
  case clang::BO_Add:
  case clang::BO_AddAssign:
    return OP_add;
  case clang::BO_Sub:
  case clang::BO_SubAssign:
    return OP_sub;
  case clang::BO_Shl:
  case clang::BO_ShlAssign:
    return OP_shl;
  case clang::BO_Shr:
  case clang::BO_ShrAssign:
    if (isSigned)
      return OP_ashr;
    else
      return OP_lshr;
  case clang::BO_Cmp:
    return OP_cmp;
  case clang::BO_LT:
    return OP_lt;
  case clang::BO_GT:
    return OP_gt;
  case clang::BO_LE:
    return OP_le;
  case clang::BO_GE:
    return OP_ge;
  case clang::BO_EQ:
    return OP_eq;
  case clang::BO_NE:
    return OP_ne;
  case clang::BO_And:
  case clang::BO_AndAssign:
    return OP_band;
  case clang::BO_Xor:
  case clang::BO_XorAssign:
    return OP_bxor;
  case clang::BO_Or:
  case clang::BO_OrAssign:
    return OP_bior;
  case clang::BO_LAnd:
    return OP_cand;
  case clang::BO_LOr:
    return OP_cior;
  default:
    return OP_undef;
  }
}

static bool isAssign(Opcode Op) {
  return Op == OP_dassign || Op == OP_iassign || kOpcodeInfo.IsCallAssigned(Op);
}

static bool isPointerType(MIRType *Ty) {
  return Ty->IsMIRPtrType() || Ty->GetKind() == kTypeArray;
}

static size_t getPointedToSize(MIRType *Ty) {
  if (Ty->IsMIRPtrType()) {
    return static_cast<MIRPtrType *>(Ty)->GetPointedType()->GetSize();
  } else if (Ty->GetKind() == kTypeArray) {
    MIRArrayType *ArrTy = static_cast<MIRArrayType *>(Ty);
#ifdef MULTIDIM_ARRAYS
    size_t Size = ArrTy->GetElemType()->GetSize();
    for (int d = 1; d < ArrTy->GetDim(); d++) {
      Size *= ArrTy->GetSizeArrayItem(d);
    }
    return Size;
#else  // !MULTIDIM_ARRAYS
    return ArrTy->GetElemType()->GetSize();
#endif // MULTIDIM_ARRAYS
  }
  ASSERT(false, "Unexpected type in getPointedToSize");
  return -1;
}

static bool isOneElementVector(clang::QualType QT) {
  return isOneElementVector(QT.getTypePtr());
}

static bool isOneElementVector(const clang::Type *Ty) {
  if (const clang::VectorType *VecTy = llvm::dyn_cast<clang::VectorType>(
          Ty->getUnqualifiedDesugaredType())) {
    if (VecTy->getNumElements() == 1) {
      return true;
    }
  }
  return false;
}
